@optimystic/db-p2p 0.24.2 → 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 +13 -4
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +17 -18
- 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 +14 -10
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +28 -58
- 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 +148 -63
- package/dist/src/libp2p-node-base.js.map +1 -1
- 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 +60 -2
- package/dist/src/peer-address-book.d.ts.map +1 -1
- package/dist/src/peer-address-book.js +89 -1
- 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 +16 -1
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +18 -16
- 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/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 +23 -20
- 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 +1204 -1235
- package/src/libp2p-node-base.ts +185 -70
- package/src/network/open-protocol-stream.ts +87 -0
- package/src/network/register-protocol-handler.ts +85 -0
- package/src/peer-address-book.ts +96 -2
- package/src/pend-validation.ts +89 -0
- package/src/reactivity/notify-transport.ts +2 -1
- package/src/reactivity/push-state-gossip.ts +2 -1
- 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 +25 -16
- package/src/reputation/types.ts +3 -1
- package/src/rn.ts +34 -4
- 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/mesh-harness.ts +524 -344
- package/src/testing/raw-storage-conformance.ts +114 -2
package/src/cohort-topic/host.ts
CHANGED
|
@@ -161,6 +161,7 @@ import { FretCohortThresholdCrypto, createVerifyOnlyThresholdCrypto } from "./th
|
|
|
161
161
|
import { FretTrustAnchor } from "./fret-trust-anchor.js";
|
|
162
162
|
import { FretSizeEstimator } from "./size-estimator.js";
|
|
163
163
|
import { peerIdToBytes, bytesToPeerIdString } from "./peer-codec.js";
|
|
164
|
+
import { registerProtocolHandler } from "../network/register-protocol-handler.js";
|
|
164
165
|
import { signPeer, verifyPeerSig } from "./peer-sig.js";
|
|
165
166
|
import { createPoWVerifier, createReputationVerifier, type BootstrapReputationView } from "./bootstrap-evidence-verifiers.js";
|
|
166
167
|
import { createParentReferenceVerifier, createDefaultParentTopicView, type BootstrapParentTopicView } from "./bootstrap-parent-reference.js";
|
|
@@ -1037,7 +1038,7 @@ export async function createCohortTopicHost(node: Libp2p, fret: FretService, opt
|
|
|
1037
1038
|
// --- protocol handlers + activity callback ---
|
|
1038
1039
|
// Await registration so the host is not returned (and dialed) before the five handlers are live —
|
|
1039
1040
|
// and, crucially, before the gossip driver below starts ticking (no tick may run on a half-wired node).
|
|
1040
|
-
await
|
|
1041
|
+
await registerCohortTopicProtocols(node, protocols, registry, dispatchRegister, childLinkDeps, minSigs, signEndorse, verifier, promoteGate, gossipTransport, maybeInstantiateColdSibling, publishSink, membershipSource, selfCoord, maxBytes);
|
|
1041
1042
|
fret.setActivityHandler(async (activity: string, cohort: string[]): Promise<{ commitCertificate: string }> => {
|
|
1042
1043
|
const decoded = decodeCohortMessage(b64urlToBytes(activity), maxBytes);
|
|
1043
1044
|
// Decode-and-branch: a `ChildLinkV1` (a child cohort registering with this parent) runs the child-link
|
|
@@ -2788,7 +2789,7 @@ function tierOfTopic(store: ReturnType<typeof createRegistrationStore>, topicId:
|
|
|
2788
2789
|
return (recs.length > 0 ? recs[0]!.tier : 0) as Tier;
|
|
2789
2790
|
}
|
|
2790
2791
|
|
|
2791
|
-
async function
|
|
2792
|
+
async function registerCohortTopicProtocols(
|
|
2792
2793
|
node: Libp2p,
|
|
2793
2794
|
protocols: CohortTopicProtocols,
|
|
2794
2795
|
registry: CoordRegistry,
|
|
@@ -2810,7 +2811,7 @@ async function registerProtocolHandlers(
|
|
|
2810
2811
|
await Promise.all([
|
|
2811
2812
|
// register: a direct dial carries a RenewV1 (ping), a ChildLinkV1 (a child cohort registering with this
|
|
2812
2813
|
// parent), or a RegisterV1 (re-attach walk fallback). The three shapes are disjoint, so try each in turn.
|
|
2813
|
-
node
|
|
2814
|
+
registerProtocolHandler(node, protocols.register, makeRequestHandler(async (frame): Promise<Uint8Array> => {
|
|
2814
2815
|
const decoded = decodeCohortMessage(frame, maxBytes);
|
|
2815
2816
|
const renew = tryValidate(() => validateRenewV1(decoded));
|
|
2816
2817
|
if (renew !== undefined) {
|
|
@@ -2830,7 +2831,7 @@ async function registerProtocolHandlers(
|
|
|
2830
2831
|
// every coord engine's bus; per-bus epoch matching governs which engine merges the record deltas.
|
|
2831
2832
|
// First, if this is a verified co-member frame for a coord we hold no engine for, instantiate that
|
|
2832
2833
|
// engine (§Cold-start instantiation) so its freshly-subscribed bus merges this very frame on `deliver`.
|
|
2833
|
-
node
|
|
2834
|
+
registerProtocolHandler(node, protocols.gossip, makeOneWayHandler(async (frame, from): Promise<void> => {
|
|
2834
2835
|
maybeInstantiateColdSibling(frame);
|
|
2835
2836
|
gossipTransport.deliver(from.toString(), frame);
|
|
2836
2837
|
}, maxBytes)),
|
|
@@ -2839,12 +2840,12 @@ async function registerProtocolHandlers(
|
|
|
2839
2840
|
// peer arrives as `from`, so the handler can gate per-(peer, topic) before any expensive work. The
|
|
2840
2841
|
// full pipeline (rate limit → findServing → effectiveAt high-water → verify+apply, bounded
|
|
2841
2842
|
// refetch) lives in the exported `handleInboundNotice`; it logs and never throws on the stream.
|
|
2842
|
-
node
|
|
2843
|
+
registerProtocolHandler(node, protocols.promote, makeOneWayHandler(async (frame, from): Promise<void> => {
|
|
2843
2844
|
await handleInboundNotice(frame, peerIdToBytes(from), registry, verifier, promoteGate, Date.now(), maxBytes);
|
|
2844
2845
|
}, maxBytes)),
|
|
2845
2846
|
|
|
2846
2847
|
// membership: serve this node's latest published cert; cache any cert the requester returns.
|
|
2847
|
-
node
|
|
2848
|
+
registerProtocolHandler(node, protocols.membership, makeRequestHandler(async (frame): Promise<Uint8Array> => {
|
|
2848
2849
|
void frame; // request frame is the raw coord; this node serves its own cohort cert
|
|
2849
2850
|
const latest = publishSink.latest();
|
|
2850
2851
|
if (latest !== undefined) {
|
|
@@ -2856,7 +2857,7 @@ async function registerProtocolHandlers(
|
|
|
2856
2857
|
// sign: per-member endorsement for threshold-signature assembly. Validate the request, run the
|
|
2857
2858
|
// endorsement policy, and reply with this node's peer-key signature over the request payload (or a
|
|
2858
2859
|
// refusal). One Ed25519 sign and nothing more — the cohort + epoch gate bounds who we sign for.
|
|
2859
|
-
node
|
|
2860
|
+
registerProtocolHandler(node, protocols.sign, makeRequestHandler(async (frame, from): Promise<Uint8Array> => {
|
|
2860
2861
|
const request = validateSignRequestV1(decodeCohortMessage(frame, maxBytes));
|
|
2861
2862
|
const reply = await signEndorse(request, from.toString());
|
|
2862
2863
|
return encodeCohortMessage(reply, maxBytes);
|
|
@@ -14,54 +14,23 @@
|
|
|
14
14
|
* exactly one bounded frame per stream and then closes; if a caller ever writes repeatedly on one
|
|
15
15
|
* stream, honor the flag by awaiting the stream's `'drain'` event the way FRET's `rpcRequest` does.
|
|
16
16
|
*
|
|
17
|
-
* {@link
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* which
|
|
21
|
-
*
|
|
17
|
+
* NOTE: accepted tradeoff — both calls to {@link openProtocolStream} below deliberately omit
|
|
18
|
+
* `negotiateFully`, taking libp2p's default of full multistream-select negotiation at stream-open.
|
|
19
|
+
* FRET's `openRpcStream` and `libp2p-key-network.ts#connect` both pass `negotiateFully: false`,
|
|
20
|
+
* which saves a round trip but defers an unsupported-protocol failure from stream-open to the
|
|
21
|
+
* first read — that would turn {@link sendOneWay} against a peer lacking the protocol into a
|
|
22
|
+
* silent no-op. Revisit if stream-open latency shows up in a profile.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
import type { Libp2p } from "libp2p";
|
|
25
26
|
import type { Connection, PeerId, Stream } from "@libp2p/interface";
|
|
26
27
|
import { readFramed, sendFramed } from "p2p-fret";
|
|
28
|
+
import { openProtocolStream } from "../network/open-protocol-stream.js";
|
|
29
|
+
import { registerProtocolHandler } from "../network/register-protocol-handler.js";
|
|
27
30
|
|
|
28
31
|
/** Default per-frame ceiling — matches FRET's 512 KiB maybe-act bound. */
|
|
29
32
|
export const DEFAULT_STREAM_MAX_BYTES = 512 * 1024;
|
|
30
33
|
|
|
31
|
-
/**
|
|
32
|
-
* libp2p refuses to open a protocol stream over a *limited* (circuit-relay) connection unless the
|
|
33
|
-
* caller opts in, so every peer reachable only through a relay — the steady state for browsers and
|
|
34
|
-
* NATed peers — depends on this flag. It is a harmless no-op on a direct connection.
|
|
35
|
-
*
|
|
36
|
-
* NOTE: accepted tradeoff — FRET's `openRpcStream` and `libp2p-key-network.ts#connect` also set
|
|
37
|
-
* `negotiateFully: false`; deliberately not set here. It saves a round trip but defers an
|
|
38
|
-
* unsupported-protocol failure from stream-open to the first read, which would turn
|
|
39
|
-
* {@link sendOneWay} against a peer lacking the protocol into a silent no-op. Revisit if
|
|
40
|
-
* stream-open latency shows up in a profile.
|
|
41
|
-
*/
|
|
42
|
-
const STREAM_OPTIONS = { runOnLimitedConnection: true } as const;
|
|
43
|
-
|
|
44
|
-
/** True for a circuit-relay ("limited") connection: libp2p stamps one with per-circuit `limits`;
|
|
45
|
-
* sniffing `/p2p-circuit` in the multiaddr covers transports that leave `limits` unpopulated. */
|
|
46
|
-
function isLimitedConnection(c: Connection): boolean {
|
|
47
|
-
if ((c as { limits?: unknown }).limits != null) return true;
|
|
48
|
-
return c.remoteAddr?.toString?.().includes("/p2p-circuit") ?? false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Open `protocol` to `peer`, reusing a healthy existing connection when there is one.
|
|
53
|
-
*
|
|
54
|
-
* Skips connections libp2p has not yet evicted from its index but that are no longer open, and
|
|
55
|
-
* prefers a direct connection over a relayed one — a relayed connection can be reset once the
|
|
56
|
-
* relay's per-circuit cap or reservation lapses, and after DCUtR upgrades a link to direct both
|
|
57
|
-
* briefly coexist. Falls back to the relayed connection when it is the only open path.
|
|
58
|
-
*/
|
|
59
|
-
async function openStream(node: Libp2p, peer: PeerId, protocol: string): Promise<Stream> {
|
|
60
|
-
const open = node.getConnections(peer).filter(c => c?.status === "open" && typeof c?.newStream === "function");
|
|
61
|
-
const chosen = open.find(c => !isLimitedConnection(c)) ?? open[0];
|
|
62
|
-
return chosen ? await chosen.newStream([protocol], STREAM_OPTIONS) : await node.dialProtocol(peer, [protocol], STREAM_OPTIONS);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
34
|
/**
|
|
66
35
|
* Open `protocol` to `peer`, send `frame`, and read the bounded reply frame.
|
|
67
36
|
*
|
|
@@ -69,7 +38,7 @@ async function openStream(node: Libp2p, peer: PeerId, protocol: string): Promise
|
|
|
69
38
|
* `readFramed` self-times-out at 5s and `dialProtocol` falls back to libp2p's default dial
|
|
70
39
|
* timeout (~30s) — so an unresponsive peer is slow, not hung. If a caller ever needs a tighter
|
|
71
40
|
* deadline (`membership-source.fetch` walks candidate peers *sequentially*, so its worst case is
|
|
72
|
-
* peers × dial-timeout),
|
|
41
|
+
* peers × dial-timeout), pass one through {@link openProtocolStream}'s `signal` the way
|
|
73
42
|
* `libp2p-key-network.ts#connect` does.
|
|
74
43
|
*/
|
|
75
44
|
export async function requestResponse(
|
|
@@ -81,7 +50,8 @@ export async function requestResponse(
|
|
|
81
50
|
): Promise<Uint8Array> {
|
|
82
51
|
let stream: Stream | undefined;
|
|
83
52
|
try {
|
|
84
|
-
|
|
53
|
+
// `negotiateFully` deliberately omitted — see the accepted tradeoff in the module docblock.
|
|
54
|
+
stream = await openProtocolStream(node, peer, protocol);
|
|
85
55
|
sendFramed(stream, frame);
|
|
86
56
|
await stream.close();
|
|
87
57
|
return await readFramed(stream, maxBytes);
|
|
@@ -100,7 +70,8 @@ export async function requestResponse(
|
|
|
100
70
|
export async function sendOneWay(node: Libp2p, peer: PeerId, protocol: string, frame: Uint8Array): Promise<void> {
|
|
101
71
|
let stream: Stream | undefined;
|
|
102
72
|
try {
|
|
103
|
-
|
|
73
|
+
// `negotiateFully` deliberately omitted — see the accepted tradeoff in the module docblock.
|
|
74
|
+
stream = await openProtocolStream(node, peer, protocol);
|
|
104
75
|
sendFramed(stream, frame);
|
|
105
76
|
await stream.close();
|
|
106
77
|
} finally {
|
|
@@ -128,7 +99,7 @@ export function handleRequestResponse(
|
|
|
128
99
|
handle: (frame: Uint8Array, from: PeerId) => Promise<Uint8Array | undefined>,
|
|
129
100
|
maxBytes = DEFAULT_STREAM_MAX_BYTES,
|
|
130
101
|
): void {
|
|
131
|
-
void node
|
|
102
|
+
void registerProtocolHandler(node, protocol, (stream: Stream, connection: Connection) => {
|
|
132
103
|
void (async (): Promise<void> => {
|
|
133
104
|
try {
|
|
134
105
|
const frame = await readFramed(stream, maxBytes);
|
package/src/dispute/cascade.ts
CHANGED
|
@@ -40,13 +40,6 @@ export type CollectionEnv = {
|
|
|
40
40
|
readonly collectionId: CollectionId;
|
|
41
41
|
readonly log: Log<unknown>;
|
|
42
42
|
readonly createBlockStorage: (blockId: BlockId) => IBlockStorage;
|
|
43
|
-
/**
|
|
44
|
-
* Optional per-block commit-latch runner threaded into each cascade child's {@link applyInvalidation}
|
|
45
|
-
* so the child's compensating write serializes against a concurrent commit on the same block — the
|
|
46
|
-
* same mutual-exclusion the root apply gets (see `InvalidationContext.withBlockCommitLatch`). Omitted
|
|
47
|
-
* → child writes run unlatched (today's behavior).
|
|
48
|
-
*/
|
|
49
|
-
readonly withBlockCommitLatch?: <T>(blockId: BlockId, fn: () => Promise<T>) => Promise<T>;
|
|
50
43
|
};
|
|
51
44
|
|
|
52
45
|
// ─── Invalidated (blockId, revision) pairs ───
|
|
@@ -213,7 +206,13 @@ export type CascadeEscalation = {
|
|
|
213
206
|
readonly collections: ReadonlyArray<CollectionId>;
|
|
214
207
|
/** Read-dependents left un-cascaded at the horizon — surfaced, never silently dropped. */
|
|
215
208
|
readonly remainder: ReadonlyArray<CascadeStanding>;
|
|
216
|
-
/**
|
|
209
|
+
/**
|
|
210
|
+
* Candidates whose fate could not be decided — a legacy entry with no persisted read set (no engine
|
|
211
|
+
* to re-execute it), or one the cascade decided to invalidate but whose `applyInvalidation` did not
|
|
212
|
+
* apply (a wholesale-refused compensating write, reason `stale-revision`; any other non-applied
|
|
213
|
+
* reason lands here too), so the reversal did not happen and the dependent must not be dropped from
|
|
214
|
+
* the frontier as if it were fine.
|
|
215
|
+
*/
|
|
217
216
|
readonly unevaluable: ReadonlyArray<CascadeStanding>;
|
|
218
217
|
};
|
|
219
218
|
|
|
@@ -290,6 +289,18 @@ export async function cascadeInvalidate(input: CascadeInput): Promise<CascadeRes
|
|
|
290
289
|
const children: CascadeChild[] = [];
|
|
291
290
|
const affectedCollections = new Set<CollectionId>(input.seed.map(s => s.collectionId));
|
|
292
291
|
const unevaluable: CascadeStanding[] = [];
|
|
292
|
+
/**
|
|
293
|
+
* Park a candidate whose fate the cascade could not decide, at most once. Both callers below mean
|
|
294
|
+
* the same thing — this dependent is neither retained nor reverted, so it must not leave the
|
|
295
|
+
* frontier as if it were fine — and both must mark its collection affected, or the `unevaluable`
|
|
296
|
+
* escalation would not name the collection that needs the re-sync.
|
|
297
|
+
*/
|
|
298
|
+
const markUnevaluable = (cand: CascadeCandidate) => {
|
|
299
|
+
if (!unevaluable.some(u => u.collectionId === cand.collectionId && u.actionId === cand.actionId)) {
|
|
300
|
+
unevaluable.push(standing(cand));
|
|
301
|
+
}
|
|
302
|
+
affectedCollections.add(cand.collectionId);
|
|
303
|
+
};
|
|
293
304
|
let retained: CascadeStanding[] = [];
|
|
294
305
|
let rounds = 0;
|
|
295
306
|
let escalation: CascadeEscalation | undefined;
|
|
@@ -335,10 +346,7 @@ export async function cascadeInvalidate(input: CascadeInput): Promise<CascadeRes
|
|
|
335
346
|
continue;
|
|
336
347
|
}
|
|
337
348
|
if (verdict === 'unevaluable') {
|
|
338
|
-
|
|
339
|
-
unevaluable.push(standing(cand));
|
|
340
|
-
}
|
|
341
|
-
affectedCollections.add(cand.collectionId);
|
|
349
|
+
markUnevaluable(cand);
|
|
342
350
|
continue;
|
|
343
351
|
}
|
|
344
352
|
|
|
@@ -354,7 +362,7 @@ export async function cascadeInvalidate(input: CascadeInput): Promise<CascadeRes
|
|
|
354
362
|
|
|
355
363
|
const env = cand.env;
|
|
356
364
|
const result = await applyInvalidation(
|
|
357
|
-
{ log: env.log, createBlockStorage: env.createBlockStorage
|
|
365
|
+
{ log: env.log, createBlockStorage: env.createBlockStorage },
|
|
358
366
|
{
|
|
359
367
|
invalidatedActionId: cand.actionId,
|
|
360
368
|
invalidatedRev: cand.rev,
|
|
@@ -370,9 +378,17 @@ export async function cascadeInvalidate(input: CascadeInput): Promise<CascadeRes
|
|
|
370
378
|
// exists (prior cascade run / restart). Treat it as invalidated and reuse its reverted blocks
|
|
371
379
|
// so the frontier still grows and the cascade reconverges without a second entry.
|
|
372
380
|
if (!result.applied && result.reason !== 'already-applied') {
|
|
373
|
-
//
|
|
374
|
-
//
|
|
381
|
+
// The child was decided-to-invalidate but the reversal did not happen, so we do NOT know
|
|
382
|
+
// whether this dependent still holds — it must not be dropped from the frontier as if it
|
|
383
|
+
// were fine. Deliberately keyed on "not applied", not on a specific reason, so a reason
|
|
384
|
+
// added later cannot re-open the silent-drop hole:
|
|
385
|
+
// - `stale-revision` — the compensating write was refused wholesale (nothing written,
|
|
386
|
+
// nothing appended). The reachable case.
|
|
387
|
+
// - `invalid-certificate` — should be impossible here (the child verifies the ROOT proof
|
|
388
|
+
// against the root's target, and that proof is a valid challenger-wins cert), but if it
|
|
389
|
+
// ever fires, an undecided dependent is the honest report.
|
|
375
390
|
log('child-apply-rejected actionId=%s reason=%s', cand.actionId, result.reason);
|
|
391
|
+
markUnevaluable(cand);
|
|
376
392
|
continue;
|
|
377
393
|
}
|
|
378
394
|
|