@optimystic/db-p2p 0.13.5 → 0.14.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 +6 -6
- package/dist/src/cluster/block-transfer-service.d.ts +67 -8
- package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer-service.js +101 -62
- package/dist/src/cluster/block-transfer-service.js.map +1 -1
- package/dist/src/cluster/client.d.ts +18 -1
- package/dist/src/cluster/client.d.ts.map +1 -1
- package/dist/src/cluster/client.js +55 -39
- package/dist/src/cluster/client.js.map +1 -1
- package/dist/src/cluster/cluster-error.d.ts +38 -0
- package/dist/src/cluster/cluster-error.d.ts.map +1 -0
- package/dist/src/cluster/cluster-error.js +51 -0
- package/dist/src/cluster/cluster-error.js.map +1 -0
- package/dist/src/cluster/cluster-repo.d.ts +135 -3
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +333 -58
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/commit-cert.d.ts +63 -0
- package/dist/src/cluster/commit-cert.d.ts.map +1 -0
- package/dist/src/cluster/commit-cert.js +108 -0
- package/dist/src/cluster/commit-cert.js.map +1 -0
- package/dist/src/cluster/service.d.ts +46 -11
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +97 -33
- package/dist/src/cluster/service.js.map +1 -1
- package/dist/src/cluster/spread-on-churn.d.ts +13 -0
- package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
- package/dist/src/cluster/spread-on-churn.js +34 -4
- package/dist/src/cluster/spread-on-churn.js.map +1 -1
- package/dist/src/cohort-topic/bootstrap-evidence-builder.d.ts +59 -0
- package/dist/src/cohort-topic/bootstrap-evidence-builder.d.ts.map +1 -0
- package/dist/src/cohort-topic/bootstrap-evidence-builder.js +79 -0
- package/dist/src/cohort-topic/bootstrap-evidence-builder.js.map +1 -0
- package/dist/src/cohort-topic/bootstrap-evidence-verifiers.d.ts +81 -0
- package/dist/src/cohort-topic/bootstrap-evidence-verifiers.d.ts.map +1 -0
- package/dist/src/cohort-topic/bootstrap-evidence-verifiers.js +93 -0
- package/dist/src/cohort-topic/bootstrap-evidence-verifiers.js.map +1 -0
- package/dist/src/cohort-topic/bootstrap-parent-reference.d.ts +108 -0
- package/dist/src/cohort-topic/bootstrap-parent-reference.d.ts.map +1 -0
- package/dist/src/cohort-topic/bootstrap-parent-reference.js +108 -0
- package/dist/src/cohort-topic/bootstrap-parent-reference.js.map +1 -0
- package/dist/src/cohort-topic/change-bridge.d.ts +65 -0
- package/dist/src/cohort-topic/change-bridge.d.ts.map +1 -0
- package/dist/src/cohort-topic/change-bridge.js +76 -0
- package/dist/src/cohort-topic/change-bridge.js.map +1 -0
- package/dist/src/cohort-topic/cohort-gossip-driver.d.ts +74 -0
- package/dist/src/cohort-topic/cohort-gossip-driver.d.ts.map +1 -0
- package/dist/src/cohort-topic/cohort-gossip-driver.js +87 -0
- package/dist/src/cohort-topic/cohort-gossip-driver.js.map +1 -0
- package/dist/src/cohort-topic/cohort-gossip-transport.d.ts +42 -0
- package/dist/src/cohort-topic/cohort-gossip-transport.d.ts.map +1 -0
- package/dist/src/cohort-topic/cohort-gossip-transport.js +57 -0
- package/dist/src/cohort-topic/cohort-gossip-transport.js.map +1 -0
- package/dist/src/cohort-topic/fret-trust-anchor.d.ts +100 -0
- package/dist/src/cohort-topic/fret-trust-anchor.d.ts.map +1 -0
- package/dist/src/cohort-topic/fret-trust-anchor.js +115 -0
- package/dist/src/cohort-topic/fret-trust-anchor.js.map +1 -0
- package/dist/src/cohort-topic/host.d.ts +573 -0
- package/dist/src/cohort-topic/host.d.ts.map +1 -0
- package/dist/src/cohort-topic/host.js +1460 -0
- package/dist/src/cohort-topic/host.js.map +1 -0
- package/dist/src/cohort-topic/index.d.ts +14 -0
- package/dist/src/cohort-topic/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/index.js +14 -0
- package/dist/src/cohort-topic/index.js.map +1 -0
- package/dist/src/cohort-topic/membership-publish-sink.d.ts +14 -0
- package/dist/src/cohort-topic/membership-publish-sink.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership-publish-sink.js +17 -0
- package/dist/src/cohort-topic/membership-publish-sink.js.map +1 -0
- package/dist/src/cohort-topic/membership-source.d.ts +36 -0
- package/dist/src/cohort-topic/membership-source.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership-source.js +58 -0
- package/dist/src/cohort-topic/membership-source.js.map +1 -0
- package/dist/src/cohort-topic/peer-codec.d.ts +18 -0
- package/dist/src/cohort-topic/peer-codec.d.ts.map +1 -0
- package/dist/src/cohort-topic/peer-codec.js +26 -0
- package/dist/src/cohort-topic/peer-codec.js.map +1 -0
- package/dist/src/cohort-topic/peer-sig.d.ts +52 -0
- package/dist/src/cohort-topic/peer-sig.d.ts.map +1 -0
- package/dist/src/cohort-topic/peer-sig.js +83 -0
- package/dist/src/cohort-topic/peer-sig.js.map +1 -0
- package/dist/src/cohort-topic/protocols.d.ts +49 -0
- package/dist/src/cohort-topic/protocols.d.ts.map +1 -0
- package/dist/src/cohort-topic/protocols.js +58 -0
- package/dist/src/cohort-topic/protocols.js.map +1 -0
- package/dist/src/cohort-topic/reactivity-membership-gate.d.ts +49 -0
- package/dist/src/cohort-topic/reactivity-membership-gate.d.ts.map +1 -0
- package/dist/src/cohort-topic/reactivity-membership-gate.js +42 -0
- package/dist/src/cohort-topic/reactivity-membership-gate.js.map +1 -0
- package/dist/src/cohort-topic/size-estimator.d.ts +16 -0
- package/dist/src/cohort-topic/size-estimator.d.ts.map +1 -0
- package/dist/src/cohort-topic/size-estimator.js +16 -0
- package/dist/src/cohort-topic/size-estimator.js.map +1 -0
- package/dist/src/cohort-topic/stream-util.d.ts +19 -0
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -0
- package/dist/src/cohort-topic/stream-util.js +76 -0
- package/dist/src/cohort-topic/stream-util.js.map +1 -0
- package/dist/src/cohort-topic/threshold-crypto.d.ts +94 -0
- package/dist/src/cohort-topic/threshold-crypto.d.ts.map +1 -0
- package/dist/src/cohort-topic/threshold-crypto.js +178 -0
- package/dist/src/cohort-topic/threshold-crypto.js.map +1 -0
- package/dist/src/cohort-topic/topic-router.d.ts +41 -0
- package/dist/src/cohort-topic/topic-router.d.ts.map +1 -0
- package/dist/src/cohort-topic/topic-router.js +63 -0
- package/dist/src/cohort-topic/topic-router.js.map +1 -0
- package/dist/src/dispute/cascade.d.ts +179 -0
- package/dist/src/dispute/cascade.d.ts.map +1 -0
- package/dist/src/dispute/cascade.js +315 -0
- package/dist/src/dispute/cascade.js.map +1 -0
- package/dist/src/dispute/client.d.ts +2 -1
- package/dist/src/dispute/client.d.ts.map +1 -1
- package/dist/src/dispute/client.js +12 -3
- package/dist/src/dispute/client.js.map +1 -1
- package/dist/src/dispute/dispute-service.d.ts +41 -1
- package/dist/src/dispute/dispute-service.d.ts.map +1 -1
- package/dist/src/dispute/dispute-service.js +147 -21
- package/dist/src/dispute/dispute-service.js.map +1 -1
- package/dist/src/dispute/index.d.ts +2 -0
- package/dist/src/dispute/index.d.ts.map +1 -1
- package/dist/src/dispute/index.js +2 -0
- package/dist/src/dispute/index.js.map +1 -1
- package/dist/src/dispute/invalidation.d.ts +266 -0
- package/dist/src/dispute/invalidation.d.ts.map +1 -0
- package/dist/src/dispute/invalidation.js +405 -0
- package/dist/src/dispute/invalidation.js.map +1 -0
- package/dist/src/dispute/types.d.ts +14 -0
- package/dist/src/dispute/types.d.ts.map +1 -1
- package/dist/src/dispute/types.js.map +1 -1
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +7 -0
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +49 -5
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +45 -3
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +522 -5
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/matchmaking/aggregate-counts.d.ts +67 -0
- package/dist/src/matchmaking/aggregate-counts.d.ts.map +1 -0
- package/dist/src/matchmaking/aggregate-counts.js +60 -0
- package/dist/src/matchmaking/aggregate-counts.js.map +1 -0
- package/dist/src/matchmaking/index.d.ts +20 -0
- package/dist/src/matchmaking/index.d.ts.map +1 -0
- package/dist/src/matchmaking/index.js +20 -0
- package/dist/src/matchmaking/index.js.map +1 -0
- package/dist/src/matchmaking/module.d.ts +181 -0
- package/dist/src/matchmaking/module.d.ts.map +1 -0
- package/dist/src/matchmaking/module.js +221 -0
- package/dist/src/matchmaking/module.js.map +1 -0
- package/dist/src/matchmaking/protocols.d.ts +37 -0
- package/dist/src/matchmaking/protocols.d.ts.map +1 -0
- package/dist/src/matchmaking/protocols.js +42 -0
- package/dist/src/matchmaking/protocols.js.map +1 -0
- package/dist/src/matchmaking/provider-manager.d.ts +52 -0
- package/dist/src/matchmaking/provider-manager.d.ts.map +1 -0
- package/dist/src/matchmaking/provider-manager.js +74 -0
- package/dist/src/matchmaking/provider-manager.js.map +1 -0
- package/dist/src/matchmaking/query-handler.d.ts +34 -0
- package/dist/src/matchmaking/query-handler.d.ts.map +1 -0
- package/dist/src/matchmaking/query-handler.js +62 -0
- package/dist/src/matchmaking/query-handler.js.map +1 -0
- package/dist/src/matchmaking/query-transport.d.ts +180 -0
- package/dist/src/matchmaking/query-transport.d.ts.map +1 -0
- package/dist/src/matchmaking/query-transport.js +315 -0
- package/dist/src/matchmaking/query-transport.js.map +1 -0
- package/dist/src/matchmaking/seeker-manager.d.ts +39 -0
- package/dist/src/matchmaking/seeker-manager.d.ts.map +1 -0
- package/dist/src/matchmaking/seeker-manager.js +49 -0
- package/dist/src/matchmaking/seeker-manager.js.map +1 -0
- package/dist/src/matchmaking/seeker-walk-client.d.ts +127 -0
- package/dist/src/matchmaking/seeker-walk-client.d.ts.map +1 -0
- package/dist/src/matchmaking/seeker-walk-client.js +189 -0
- package/dist/src/matchmaking/seeker-walk-client.js.map +1 -0
- package/dist/src/matchmaking/traffic-validation.d.ts +128 -0
- package/dist/src/matchmaking/traffic-validation.d.ts.map +1 -0
- package/dist/src/matchmaking/traffic-validation.js +105 -0
- package/dist/src/matchmaking/traffic-validation.js.map +1 -0
- package/dist/src/network/network-manager-service.d.ts +4 -2
- package/dist/src/network/network-manager-service.d.ts.map +1 -1
- package/dist/src/network/network-manager-service.js +9 -3
- package/dist/src/network/network-manager-service.js.map +1 -1
- package/dist/src/protocol-client.d.ts +13 -0
- package/dist/src/protocol-client.d.ts.map +1 -1
- package/dist/src/protocol-client.js +74 -2
- package/dist/src/protocol-client.js.map +1 -1
- package/dist/src/reactivity/forwarder-host.d.ts +198 -0
- package/dist/src/reactivity/forwarder-host.d.ts.map +1 -0
- package/dist/src/reactivity/forwarder-host.js +355 -0
- package/dist/src/reactivity/forwarder-host.js.map +1 -0
- package/dist/src/reactivity/index.d.ts +19 -0
- package/dist/src/reactivity/index.d.ts.map +1 -0
- package/dist/src/reactivity/index.js +19 -0
- package/dist/src/reactivity/index.js.map +1 -0
- package/dist/src/reactivity/notify-transport.d.ts +70 -0
- package/dist/src/reactivity/notify-transport.d.ts.map +1 -0
- package/dist/src/reactivity/notify-transport.js +111 -0
- package/dist/src/reactivity/notify-transport.js.map +1 -0
- package/dist/src/reactivity/origination-manager.d.ts +110 -0
- package/dist/src/reactivity/origination-manager.d.ts.map +1 -0
- package/dist/src/reactivity/origination-manager.js +129 -0
- package/dist/src/reactivity/origination-manager.js.map +1 -0
- package/dist/src/reactivity/protocols.d.ts +43 -0
- package/dist/src/reactivity/protocols.d.ts.map +1 -0
- package/dist/src/reactivity/protocols.js +50 -0
- package/dist/src/reactivity/protocols.js.map +1 -0
- package/dist/src/reactivity/push-state-gossip.d.ts +132 -0
- package/dist/src/reactivity/push-state-gossip.d.ts.map +1 -0
- package/dist/src/reactivity/push-state-gossip.js +231 -0
- package/dist/src/reactivity/push-state-gossip.js.map +1 -0
- package/dist/src/reactivity/recover-transport.d.ts +167 -0
- package/dist/src/reactivity/recover-transport.d.ts.map +1 -0
- package/dist/src/reactivity/recover-transport.js +308 -0
- package/dist/src/reactivity/recover-transport.js.map +1 -0
- package/dist/src/reactivity/rotation-rereg-scheduler.d.ts +142 -0
- package/dist/src/reactivity/rotation-rereg-scheduler.d.ts.map +1 -0
- package/dist/src/reactivity/rotation-rereg-scheduler.js +223 -0
- package/dist/src/reactivity/rotation-rereg-scheduler.js.map +1 -0
- package/dist/src/reactivity/subscriber-registry.d.ts +47 -0
- package/dist/src/reactivity/subscriber-registry.d.ts.map +1 -0
- package/dist/src/reactivity/subscriber-registry.js +87 -0
- package/dist/src/reactivity/subscriber-registry.js.map +1 -0
- package/dist/src/reactivity/subscription-manager.d.ts +221 -0
- package/dist/src/reactivity/subscription-manager.d.ts.map +1 -0
- package/dist/src/reactivity/subscription-manager.js +302 -0
- package/dist/src/reactivity/subscription-manager.js.map +1 -0
- package/dist/src/reactivity/topic-bytes.d.ts +32 -0
- package/dist/src/reactivity/topic-bytes.d.ts.map +1 -0
- package/dist/src/reactivity/topic-bytes.js +34 -0
- package/dist/src/reactivity/topic-bytes.js.map +1 -0
- package/dist/src/repo/client.d.ts.map +1 -1
- package/dist/src/repo/client.js +51 -23
- package/dist/src/repo/client.js.map +1 -1
- package/dist/src/repo/service.d.ts +46 -1
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +79 -45
- package/dist/src/repo/service.js.map +1 -1
- package/dist/src/rpc-deadline.d.ts +37 -0
- package/dist/src/rpc-deadline.d.ts.map +1 -0
- package/dist/src/rpc-deadline.js +31 -0
- package/dist/src/rpc-deadline.js.map +1 -0
- package/dist/src/storage/block-storage.d.ts +2 -0
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +95 -2
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +25 -0
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +53 -2
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +193 -9
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/sync/client.d.ts +4 -1
- package/dist/src/sync/client.d.ts.map +1 -1
- package/dist/src/sync/client.js +5 -2
- package/dist/src/sync/client.js.map +1 -1
- package/dist/src/testing/cohort-topic-mesh-harness.d.ts +219 -0
- package/dist/src/testing/cohort-topic-mesh-harness.d.ts.map +1 -0
- package/dist/src/testing/cohort-topic-mesh-harness.js +467 -0
- package/dist/src/testing/cohort-topic-mesh-harness.js.map +1 -0
- package/dist/src/testing/matchmaking-mesh-harness.d.ts +187 -0
- package/dist/src/testing/matchmaking-mesh-harness.d.ts.map +1 -0
- package/dist/src/testing/matchmaking-mesh-harness.js +358 -0
- package/dist/src/testing/matchmaking-mesh-harness.js.map +1 -0
- package/dist/src/testing/mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.js +23 -1
- package/dist/src/testing/mesh-harness.js.map +1 -1
- package/dist/src/testing/reactivity-mesh-harness.d.ts +298 -0
- package/dist/src/testing/reactivity-mesh-harness.d.ts.map +1 -0
- package/dist/src/testing/reactivity-mesh-harness.js +663 -0
- package/dist/src/testing/reactivity-mesh-harness.js.map +1 -0
- package/package.json +7 -4
- package/src/cluster/block-transfer-service.ts +133 -68
- package/src/cluster/client.ts +64 -45
- package/src/cluster/cluster-error.ts +64 -0
- package/src/cluster/cluster-repo.ts +405 -55
- package/src/cluster/commit-cert.ts +139 -0
- package/src/cluster/service.ts +116 -44
- package/src/cluster/spread-on-churn.ts +328 -285
- package/src/cohort-topic/bootstrap-evidence-builder.ts +122 -0
- package/src/cohort-topic/bootstrap-evidence-verifiers.ts +132 -0
- package/src/cohort-topic/bootstrap-parent-reference.ts +159 -0
- package/src/cohort-topic/change-bridge.ts +109 -0
- package/src/cohort-topic/cohort-gossip-driver.ts +144 -0
- package/src/cohort-topic/cohort-gossip-transport.ts +74 -0
- package/src/cohort-topic/fret-trust-anchor.ts +153 -0
- package/src/cohort-topic/host.ts +2175 -0
- package/src/cohort-topic/index.ts +13 -0
- package/src/cohort-topic/membership-publish-sink.ts +20 -0
- package/src/cohort-topic/membership-source.ts +68 -0
- package/src/cohort-topic/peer-codec.ts +31 -0
- package/src/cohort-topic/peer-sig.ts +86 -0
- package/src/cohort-topic/protocols.ts +71 -0
- package/src/cohort-topic/reactivity-membership-gate.ts +77 -0
- package/src/cohort-topic/size-estimator.ts +16 -0
- package/src/cohort-topic/stream-util.ts +87 -0
- package/src/cohort-topic/threshold-crypto.ts +239 -0
- package/src/cohort-topic/topic-router.ts +77 -0
- package/src/dispute/cascade.ts +517 -0
- package/src/dispute/client.ts +12 -2
- package/src/dispute/dispute-service.ts +171 -24
- package/src/dispute/index.ts +42 -0
- package/src/dispute/invalidation.ts +587 -0
- package/src/dispute/types.ts +14 -0
- package/src/index.ts +4 -0
- package/src/libp2p-key-network.ts +49 -6
- package/src/libp2p-node-base.ts +620 -12
- package/src/matchmaking/aggregate-counts.ts +104 -0
- package/src/matchmaking/index.ts +20 -0
- package/src/matchmaking/module.ts +363 -0
- package/src/matchmaking/protocols.ts +51 -0
- package/src/matchmaking/provider-manager.ts +95 -0
- package/src/matchmaking/query-handler.ts +88 -0
- package/src/matchmaking/query-transport.ts +483 -0
- package/src/matchmaking/seeker-manager.ts +64 -0
- package/src/matchmaking/seeker-walk-client.ts +293 -0
- package/src/matchmaking/traffic-validation.ts +195 -0
- package/src/network/network-manager-service.ts +9 -2
- package/src/protocol-client.ts +64 -3
- package/src/reactivity/forwarder-host.ts +438 -0
- package/src/reactivity/index.ts +19 -0
- package/src/reactivity/notify-transport.ts +144 -0
- package/src/reactivity/origination-manager.ts +192 -0
- package/src/reactivity/protocols.ts +61 -0
- package/src/reactivity/push-state-gossip.ts +291 -0
- package/src/reactivity/recover-transport.ts +408 -0
- package/src/reactivity/rotation-rereg-scheduler.ts +256 -0
- package/src/reactivity/subscriber-registry.ts +96 -0
- package/src/reactivity/subscription-manager.ts +450 -0
- package/src/reactivity/topic-bytes.ts +37 -0
- package/src/repo/client.ts +54 -23
- package/src/repo/service.ts +90 -42
- package/src/rpc-deadline.ts +45 -0
- package/src/storage/block-storage.ts +106 -3
- package/src/storage/i-block-storage.ts +27 -0
- package/src/storage/storage-repo.ts +209 -13
- package/src/sync/client.ts +5 -2
- package/src/testing/cohort-topic-mesh-harness.ts +613 -0
- package/src/testing/matchmaking-mesh-harness.ts +475 -0
- package/src/testing/mesh-harness.ts +22 -2
- package/src/testing/reactivity-mesh-harness.ts +922 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ActionId, ClusterRecord, CollectionChangeEvent, CommitCert } from "@optimystic/db-core";
|
|
2
|
+
/**
|
|
3
|
+
* Build the {@link CommitCert} for a consensus-committed action from its {@link ClusterRecord}.
|
|
4
|
+
*
|
|
5
|
+
* The threshold signature is the cluster's `approve` **commit** signatures — the per-member Ed25519
|
|
6
|
+
* commit votes the cohort already produced over the commit hash — concatenated in ascending
|
|
7
|
+
* signer-id order, so the blob is reproducible and `signers[i]` aligns with chunk `i` (the
|
|
8
|
+
* collected-multisig convention the cohort-topic verifier uses). This is the **authoritative**
|
|
9
|
+
* commit cert: reactivity reuses these exact bytes as a notification's signature and never re-signs.
|
|
10
|
+
*
|
|
11
|
+
* `signedPayload` is the exact byte preimage every approving member signed — the cluster commit-vote
|
|
12
|
+
* payload `utf8(commitHash + ":approve")`, identical across signers. The caller supplies it (this stays
|
|
13
|
+
* pure and does **not** recompute the commit hash, since the canonical-JSON helper that derives it is
|
|
14
|
+
* private to `ClusterMember` and duplicating it here risks drift). Reactivity sets a notification's
|
|
15
|
+
* `digest` to base64url(signedPayload) so a subscriber's threshold-verify over `digest` reproduces the
|
|
16
|
+
* exact signed image. Pure.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildCommitCert(record: ClusterRecord, minSigs: number, signedPayload: Uint8Array): CommitCert;
|
|
19
|
+
/**
|
|
20
|
+
* The deterministic change-event/notification `actionId` for a durable invalidation, derived from the
|
|
21
|
+
* `(invalidatedActionId, disputeId)` pair that uniquely identifies the reversal. An invalidation has no
|
|
22
|
+
* transaction `actionId` of its own (it reverses one), but reactivity keys both the captured commit cert
|
|
23
|
+
* and the emitted {@link CollectionChangeEvent} by an `actionId` so the bridge's cert extractor can
|
|
24
|
+
* resolve the reused cert. Both the cluster member's cert capture and the invalidation-apply sink that
|
|
25
|
+
* emits the change event derive this same id from the same pair, closing the loop. Distinct from the
|
|
26
|
+
* per-block deterministic revert `actionId` in `dispute/invalidation.ts` (which also carries blockId+rev);
|
|
27
|
+
* this is the single transaction-level id for the whole reversal.
|
|
28
|
+
*/
|
|
29
|
+
export declare function invalidationActionId(invalidatedActionId: ActionId, disputeId: string): ActionId;
|
|
30
|
+
/** Default retention for a captured commit cert — long enough for the post-commit change event to
|
|
31
|
+
* resolve it, short enough that the store stays bounded under sustained commit load. */
|
|
32
|
+
export declare const DEFAULT_COMMIT_CERT_TTL_MS = 60000;
|
|
33
|
+
/** Default cap on retained certs — a backstop against unbounded growth if commits ever stop emitting. */
|
|
34
|
+
export declare const DEFAULT_COMMIT_CERT_MAX_ENTRIES = 4096;
|
|
35
|
+
/**
|
|
36
|
+
* Bounded, TTL'd cache of recently-committed {@link CommitCert}s keyed by `actionId`. The cluster
|
|
37
|
+
* member records a cert here just before consensus applies the commit to local storage; the
|
|
38
|
+
* change-notifier bridge's extractor reads it when the StorageRepo emits the matching
|
|
39
|
+
* {@link CollectionChangeEvent}. Both happen on the same node within one commit, so the window is
|
|
40
|
+
* tiny — but a TTL + cap keep the map from leaking if a commit ever lands without emitting a change
|
|
41
|
+
* event (e.g. an idempotent re-commit, or a path with no catch-all subscriber).
|
|
42
|
+
*/
|
|
43
|
+
export interface CommitCertStore {
|
|
44
|
+
/** Record `cert` for `actionId` (overwrites any prior entry). `now` is unix ms (injectable for tests). */
|
|
45
|
+
put(actionId: ActionId, cert: CommitCert, now?: number): void;
|
|
46
|
+
/** The cert for `actionId` if still retained, else `undefined`. */
|
|
47
|
+
get(actionId: ActionId, now?: number): CommitCert | undefined;
|
|
48
|
+
}
|
|
49
|
+
export interface CommitCertStoreOptions {
|
|
50
|
+
/** Retention per entry (ms). Default {@link DEFAULT_COMMIT_CERT_TTL_MS}. */
|
|
51
|
+
readonly ttlMs?: number;
|
|
52
|
+
/** Maximum retained entries (oldest evicted first). Default {@link DEFAULT_COMMIT_CERT_MAX_ENTRIES}. */
|
|
53
|
+
readonly maxEntries?: number;
|
|
54
|
+
}
|
|
55
|
+
/** Build an in-memory {@link CommitCertStore} (insertion-ordered Map → recency for eviction). */
|
|
56
|
+
export declare function createCommitCertStore(options?: CommitCertStoreOptions): CommitCertStore;
|
|
57
|
+
/**
|
|
58
|
+
* The bridge's `extractCommitCert` over a {@link CommitCertStore}: resolve a change event's committed
|
|
59
|
+
* action to the cluster commit cert captured for it. Returns `undefined` (logged) when no cert is
|
|
60
|
+
* retained — the bridge then skips origination rather than fabricating an unsigned notification.
|
|
61
|
+
*/
|
|
62
|
+
export declare function makeClusterCommitCertExtractor(store: CommitCertStore): (event: CollectionChangeEvent) => CommitCert | undefined;
|
|
63
|
+
//# sourceMappingURL=commit-cert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-cert.d.ts","sourceRoot":"","sources":["../../../src/cluster/commit-cert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMtG;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,GAAG,UAAU,CAO7G;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,mBAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,CAE/F;AAaD;wFACwF;AACxF,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD,yGAAyG;AACzG,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAEpD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC/B,0GAA0G;IAC1G,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D,mEAAmE;IACnE,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CAC9D;AAED,MAAM,WAAW,sBAAsB;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,wGAAwG;IACxG,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,iGAAiG;AACjG,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CAqC3F;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,eAAe,GAAG,CAAC,KAAK,EAAE,qBAAqB,KAAK,UAAU,GAAG,SAAS,CAQ/H"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays';
|
|
2
|
+
import { createLogger } from "../logger.js";
|
|
3
|
+
const log = createLogger('commit-cert');
|
|
4
|
+
/**
|
|
5
|
+
* Build the {@link CommitCert} for a consensus-committed action from its {@link ClusterRecord}.
|
|
6
|
+
*
|
|
7
|
+
* The threshold signature is the cluster's `approve` **commit** signatures — the per-member Ed25519
|
|
8
|
+
* commit votes the cohort already produced over the commit hash — concatenated in ascending
|
|
9
|
+
* signer-id order, so the blob is reproducible and `signers[i]` aligns with chunk `i` (the
|
|
10
|
+
* collected-multisig convention the cohort-topic verifier uses). This is the **authoritative**
|
|
11
|
+
* commit cert: reactivity reuses these exact bytes as a notification's signature and never re-signs.
|
|
12
|
+
*
|
|
13
|
+
* `signedPayload` is the exact byte preimage every approving member signed — the cluster commit-vote
|
|
14
|
+
* payload `utf8(commitHash + ":approve")`, identical across signers. The caller supplies it (this stays
|
|
15
|
+
* pure and does **not** recompute the commit hash, since the canonical-JSON helper that derives it is
|
|
16
|
+
* private to `ClusterMember` and duplicating it here risks drift). Reactivity sets a notification's
|
|
17
|
+
* `digest` to base64url(signedPayload) so a subscriber's threshold-verify over `digest` reproduces the
|
|
18
|
+
* exact signed image. Pure.
|
|
19
|
+
*/
|
|
20
|
+
export function buildCommitCert(record, minSigs, signedPayload) {
|
|
21
|
+
const approvals = Object.entries(record.commits)
|
|
22
|
+
.filter(([, sig]) => sig.type === 'approve')
|
|
23
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
24
|
+
const signers = approvals.map(([peerId]) => peerId);
|
|
25
|
+
const thresholdSig = concatBytes(approvals.map(([, sig]) => uint8ArrayFromString(sig.signature, 'base64url')));
|
|
26
|
+
return { thresholdSig, signers, minSigs, signedPayload };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The deterministic change-event/notification `actionId` for a durable invalidation, derived from the
|
|
30
|
+
* `(invalidatedActionId, disputeId)` pair that uniquely identifies the reversal. An invalidation has no
|
|
31
|
+
* transaction `actionId` of its own (it reverses one), but reactivity keys both the captured commit cert
|
|
32
|
+
* and the emitted {@link CollectionChangeEvent} by an `actionId` so the bridge's cert extractor can
|
|
33
|
+
* resolve the reused cert. Both the cluster member's cert capture and the invalidation-apply sink that
|
|
34
|
+
* emits the change event derive this same id from the same pair, closing the loop. Distinct from the
|
|
35
|
+
* per-block deterministic revert `actionId` in `dispute/invalidation.ts` (which also carries blockId+rev);
|
|
36
|
+
* this is the single transaction-level id for the whole reversal.
|
|
37
|
+
*/
|
|
38
|
+
export function invalidationActionId(invalidatedActionId, disputeId) {
|
|
39
|
+
return `inv:${invalidatedActionId}:${disputeId}`;
|
|
40
|
+
}
|
|
41
|
+
function concatBytes(parts) {
|
|
42
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
43
|
+
const out = new Uint8Array(total);
|
|
44
|
+
let offset = 0;
|
|
45
|
+
for (const part of parts) {
|
|
46
|
+
out.set(part, offset);
|
|
47
|
+
offset += part.length;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/** Default retention for a captured commit cert — long enough for the post-commit change event to
|
|
52
|
+
* resolve it, short enough that the store stays bounded under sustained commit load. */
|
|
53
|
+
export const DEFAULT_COMMIT_CERT_TTL_MS = 60_000;
|
|
54
|
+
/** Default cap on retained certs — a backstop against unbounded growth if commits ever stop emitting. */
|
|
55
|
+
export const DEFAULT_COMMIT_CERT_MAX_ENTRIES = 4096;
|
|
56
|
+
/** Build an in-memory {@link CommitCertStore} (insertion-ordered Map → recency for eviction). */
|
|
57
|
+
export function createCommitCertStore(options = {}) {
|
|
58
|
+
const ttlMs = options.ttlMs ?? DEFAULT_COMMIT_CERT_TTL_MS;
|
|
59
|
+
const maxEntries = options.maxEntries ?? DEFAULT_COMMIT_CERT_MAX_ENTRIES;
|
|
60
|
+
const entries = new Map();
|
|
61
|
+
const dropExpired = (now) => {
|
|
62
|
+
for (const [actionId, entry] of entries) {
|
|
63
|
+
if (entry.expiresAt <= now) {
|
|
64
|
+
entries.delete(actionId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
put(actionId, cert, now = Date.now()) {
|
|
70
|
+
dropExpired(now);
|
|
71
|
+
// Re-insert so a refreshed key moves to the most-recent end (Map preserves insertion order).
|
|
72
|
+
entries.delete(actionId);
|
|
73
|
+
entries.set(actionId, { cert, expiresAt: now + ttlMs });
|
|
74
|
+
while (entries.size > maxEntries) {
|
|
75
|
+
const oldest = entries.keys().next().value;
|
|
76
|
+
if (oldest === undefined)
|
|
77
|
+
break;
|
|
78
|
+
entries.delete(oldest);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
get(actionId, now = Date.now()) {
|
|
82
|
+
const entry = entries.get(actionId);
|
|
83
|
+
if (!entry) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
if (entry.expiresAt <= now) {
|
|
87
|
+
entries.delete(actionId);
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
return entry.cert;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The bridge's `extractCommitCert` over a {@link CommitCertStore}: resolve a change event's committed
|
|
96
|
+
* action to the cluster commit cert captured for it. Returns `undefined` (logged) when no cert is
|
|
97
|
+
* retained — the bridge then skips origination rather than fabricating an unsigned notification.
|
|
98
|
+
*/
|
|
99
|
+
export function makeClusterCommitCertExtractor(store) {
|
|
100
|
+
return (event) => {
|
|
101
|
+
const cert = store.get(event.actionId);
|
|
102
|
+
if (!cert) {
|
|
103
|
+
log('no commit cert retained for actionId=%s (collection=%s rev=%d); skipping origination', event.actionId, event.collectionId, event.rev);
|
|
104
|
+
}
|
|
105
|
+
return cert;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=commit-cert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-cert.js","sourceRoot":"","sources":["../../../src/cluster/commit-cert.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { Startable, Logger, StreamHandler } from '@libp2p/interface';
|
|
2
|
-
import type { ICluster } from '@optimystic/db-core';
|
|
1
|
+
import type { Startable, Logger, StreamHandler, PeerId } from '@libp2p/interface';
|
|
2
|
+
import type { ICluster, ClusterRecord } from '@optimystic/db-core';
|
|
3
|
+
import { type RedirectPayload } from '../repo/redirect.js';
|
|
3
4
|
interface BaseComponents {
|
|
4
5
|
logger: {
|
|
5
6
|
forComponent: (name: string) => Logger;
|
|
@@ -11,6 +12,17 @@ interface BaseComponents {
|
|
|
11
12
|
}
|
|
12
13
|
export interface ClusterServiceComponents extends BaseComponents {
|
|
13
14
|
cluster: ICluster;
|
|
15
|
+
/**
|
|
16
|
+
* This node's own peer id, used to decide whether we are a member of a
|
|
17
|
+
* cluster record's peer set. When absent the service cannot scope membership
|
|
18
|
+
* and processes every update locally (no redirect).
|
|
19
|
+
*/
|
|
20
|
+
peerId?: PeerId;
|
|
21
|
+
/**
|
|
22
|
+
* Optional resolver for a peer's dialable multiaddrs, used as a fallback when
|
|
23
|
+
* a redirect target has no multiaddrs embedded in `record.peers`.
|
|
24
|
+
*/
|
|
25
|
+
getConnectionAddrs?: (peerId: PeerId) => string[];
|
|
14
26
|
}
|
|
15
27
|
export interface ClusterServiceInit {
|
|
16
28
|
protocol?: string;
|
|
@@ -18,17 +30,13 @@ export interface ClusterServiceInit {
|
|
|
18
30
|
maxInboundStreams?: number;
|
|
19
31
|
maxOutboundStreams?: number;
|
|
20
32
|
logPrefix?: string;
|
|
21
|
-
kBucketSize?: number;
|
|
22
|
-
configuredClusterSize?: number;
|
|
23
|
-
allowClusterDownsize?: boolean;
|
|
24
|
-
clusterSizeTolerance?: number;
|
|
25
33
|
/**
|
|
26
34
|
* Responsibility K - the replica set size for determining cluster membership.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* Default: 1 (only
|
|
35
|
+
* When the cluster record's peer set is smaller than this, the mesh is treated
|
|
36
|
+
* as "small" and the update is processed locally regardless of membership. When
|
|
37
|
+
* the peer set is at least this size and we are not a member, the update is
|
|
38
|
+
* redirected to the responsible peers.
|
|
39
|
+
* Default: 1 (only members process; any larger non-member set redirects)
|
|
32
40
|
*/
|
|
33
41
|
responsibilityK?: number;
|
|
34
42
|
}
|
|
@@ -44,10 +52,37 @@ export declare class ClusterService implements Startable {
|
|
|
44
52
|
private readonly cluster;
|
|
45
53
|
private readonly components;
|
|
46
54
|
private running;
|
|
55
|
+
/** Responsibility K - small-mesh bypass threshold for redirect decisions */
|
|
56
|
+
private readonly responsibilityK;
|
|
47
57
|
constructor(components: ClusterServiceComponents, init?: ClusterServiceInit);
|
|
48
58
|
readonly [Symbol.toStringTag] = "@libp2p/cluster";
|
|
59
|
+
private getSelfId;
|
|
60
|
+
private getPeerAddrs;
|
|
61
|
+
/**
|
|
62
|
+
* Decide whether this node should redirect a cluster update instead of
|
|
63
|
+
* participating in its consensus.
|
|
64
|
+
*
|
|
65
|
+
* Membership is scoped against `record.peers` — the authoritative set the
|
|
66
|
+
* coordinator already computed and embedded (it only ever dials peers in this
|
|
67
|
+
* set). Using it directly (rather than independently recomputing the cluster
|
|
68
|
+
* from the key) is regression-proof against the "empty promises" symptom: a
|
|
69
|
+
* peer the coordinator legitimately included is, by construction, present in
|
|
70
|
+
* `record.peers` and is therefore never redirected.
|
|
71
|
+
*
|
|
72
|
+
* Returns a {@link RedirectPayload} when this node is not responsible, or null
|
|
73
|
+
* when the update should be processed locally (we are a member, the mesh is too
|
|
74
|
+
* small to scope, or we lack the identity/peer set to make a decision).
|
|
75
|
+
*/
|
|
76
|
+
checkRedirect(record: ClusterRecord): RedirectPayload | null;
|
|
49
77
|
start(): Promise<void>;
|
|
50
78
|
stop(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Run a single decoded protocol message. An application-level throw
|
|
81
|
+
* (validation / signature / merge / consensus failure inside `cluster.update`)
|
|
82
|
+
* propagates to the caller, which turns it into a structured error envelope;
|
|
83
|
+
* a redirect or a successful {@link ClusterRecord} is returned as-is.
|
|
84
|
+
*/
|
|
85
|
+
private processOperation;
|
|
51
86
|
private handleIncomingStream;
|
|
52
87
|
}
|
|
53
88
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/cluster/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/cluster/service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAsB,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACtG,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAIxE,UAAU,cAAc;IACvB,MAAM,EAAE;QAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAC;IACnD,SAAS,EAAE;QACV,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAClF,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KAC7C,CAAA;CACD;AAED,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC/D,OAAO,EAAE,QAAQ,CAAA;IACjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;CACjD;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,IAAI,GAAE,kBAAuB,GAAG,CAAC,UAAU,EAAE,wBAAwB,KAAK,cAAc,CAEtH;AAED;;GAEG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IACtD,OAAO,CAAC,OAAO,CAAU;IACzB,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAE7B,UAAU,EAAE,wBAAwB,EAAE,IAAI,GAAE,kBAAuB;IAW/E,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,qBAAqB;IAElD,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,YAAY;IAmBpB;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,GAAG,IAAI;IAwBtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAatB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B;;;;;OAKG;YACW,gBAAgB;IAU9B,OAAO,CAAC,oBAAoB;CA8C5B"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { pipe } from 'it-pipe';
|
|
2
2
|
import { decode as lpDecode, encode as lpEncode } from 'it-length-prefixed';
|
|
3
|
+
import { peerIdFromString } from '@libp2p/peer-id';
|
|
4
|
+
import { encodePeers } from '../repo/redirect.js';
|
|
5
|
+
import { toClusterErrorEnvelope } from './cluster-error.js';
|
|
3
6
|
export function clusterService(init = {}) {
|
|
4
7
|
return (components) => new ClusterService(components, init);
|
|
5
8
|
}
|
|
@@ -14,6 +17,8 @@ export class ClusterService {
|
|
|
14
17
|
cluster;
|
|
15
18
|
components;
|
|
16
19
|
running;
|
|
20
|
+
/** Responsibility K - small-mesh bypass threshold for redirect decisions */
|
|
21
|
+
responsibilityK;
|
|
17
22
|
constructor(components, init = {}) {
|
|
18
23
|
this.components = components;
|
|
19
24
|
this.protocol = init.protocol ?? (init.protocolPrefix ?? '/db-p2p') + '/cluster/1.0.0';
|
|
@@ -22,8 +27,72 @@ export class ClusterService {
|
|
|
22
27
|
this.log = components.logger.forComponent(init.logPrefix ?? 'db-p2p:cluster');
|
|
23
28
|
this.cluster = components.cluster;
|
|
24
29
|
this.running = false;
|
|
30
|
+
this.responsibilityK = init.responsibilityK ?? 1;
|
|
25
31
|
}
|
|
26
32
|
[Symbol.toStringTag] = '@libp2p/cluster';
|
|
33
|
+
getSelfId() {
|
|
34
|
+
if (this.components.peerId)
|
|
35
|
+
return this.components.peerId;
|
|
36
|
+
return this.components.libp2p?.peerId;
|
|
37
|
+
}
|
|
38
|
+
getPeerAddrs(id) {
|
|
39
|
+
let pid;
|
|
40
|
+
try {
|
|
41
|
+
pid = peerIdFromString(id);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
if (this.components.getConnectionAddrs)
|
|
47
|
+
return this.components.getConnectionAddrs(pid);
|
|
48
|
+
const libp2p = this.components.libp2p;
|
|
49
|
+
if (!libp2p?.getConnections)
|
|
50
|
+
return [];
|
|
51
|
+
const conns = libp2p.getConnections(pid) ?? [];
|
|
52
|
+
const addrs = [];
|
|
53
|
+
for (const c of conns) {
|
|
54
|
+
const addr = c.remoteAddr?.toString?.();
|
|
55
|
+
if (addr)
|
|
56
|
+
addrs.push(addr);
|
|
57
|
+
}
|
|
58
|
+
return addrs;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Decide whether this node should redirect a cluster update instead of
|
|
62
|
+
* participating in its consensus.
|
|
63
|
+
*
|
|
64
|
+
* Membership is scoped against `record.peers` — the authoritative set the
|
|
65
|
+
* coordinator already computed and embedded (it only ever dials peers in this
|
|
66
|
+
* set). Using it directly (rather than independently recomputing the cluster
|
|
67
|
+
* from the key) is regression-proof against the "empty promises" symptom: a
|
|
68
|
+
* peer the coordinator legitimately included is, by construction, present in
|
|
69
|
+
* `record.peers` and is therefore never redirected.
|
|
70
|
+
*
|
|
71
|
+
* Returns a {@link RedirectPayload} when this node is not responsible, or null
|
|
72
|
+
* when the update should be processed locally (we are a member, the mesh is too
|
|
73
|
+
* small to scope, or we lack the identity/peer set to make a decision).
|
|
74
|
+
*/
|
|
75
|
+
checkRedirect(record) {
|
|
76
|
+
const selfId = this.getSelfId();
|
|
77
|
+
if (!selfId)
|
|
78
|
+
return null; // no identity → can't scope, process locally
|
|
79
|
+
const peers = record.peers ?? {};
|
|
80
|
+
const peerIds = Object.keys(peers);
|
|
81
|
+
if (peerIds.length === 0)
|
|
82
|
+
return null; // nothing to scope against → process locally
|
|
83
|
+
const selfStr = selfId.toString();
|
|
84
|
+
const isMember = peerIds.includes(selfStr);
|
|
85
|
+
const smallMesh = peerIds.length < this.responsibilityK;
|
|
86
|
+
if (!smallMesh && !isMember) {
|
|
87
|
+
const others = peerIds.filter(id => id !== selfStr);
|
|
88
|
+
return encodePeers(others.map(id => {
|
|
89
|
+
const recAddrs = peers[id]?.multiaddrs ?? [];
|
|
90
|
+
const addrs = recAddrs.length > 0 ? recAddrs : this.getPeerAddrs(id);
|
|
91
|
+
return { id, addrs };
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
27
96
|
async start() {
|
|
28
97
|
if (this.running) {
|
|
29
98
|
return;
|
|
@@ -41,49 +110,44 @@ export class ClusterService {
|
|
|
41
110
|
await this.components.registrar.unhandle(this.protocol);
|
|
42
111
|
this.running = false;
|
|
43
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Run a single decoded protocol message. An application-level throw
|
|
115
|
+
* (validation / signature / merge / consensus failure inside `cluster.update`)
|
|
116
|
+
* propagates to the caller, which turns it into a structured error envelope;
|
|
117
|
+
* a redirect or a successful {@link ClusterRecord} is returned as-is.
|
|
118
|
+
*/
|
|
119
|
+
async processOperation(message) {
|
|
120
|
+
if (message.operation === 'update') {
|
|
121
|
+
// Scope consensus to responsible peers: redirect when we are not a
|
|
122
|
+
// member of the record's authoritative peer set, otherwise process.
|
|
123
|
+
const redirect = this.checkRedirect(message.record);
|
|
124
|
+
return redirect ?? await this.cluster.update(message.record);
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`Unknown operation: ${message.operation}`);
|
|
127
|
+
}
|
|
44
128
|
handleIncomingStream(stream, connection) {
|
|
45
129
|
const peerId = connection.remotePeer;
|
|
46
130
|
const processStream = async function* (source) {
|
|
47
131
|
for await (const msg of source) {
|
|
48
|
-
// Decode the message
|
|
132
|
+
// Decode the framing. A malformed/undecodable message is a transport
|
|
133
|
+
// fault handled by the outer abort path, not an application error.
|
|
49
134
|
const decoded = new TextDecoder().decode(msg.subarray());
|
|
50
135
|
const message = JSON.parse(decoded);
|
|
51
|
-
//
|
|
136
|
+
// Application-level processing: surface any throw to the coordinator as
|
|
137
|
+
// a structured error envelope (closing the stream normally) instead of
|
|
138
|
+
// aborting, so the real cause — not an opaque StreamResetError — reaches
|
|
139
|
+
// the coordinator, which already enables debug logging. The abort path
|
|
140
|
+
// is reserved for genuinely unrecoverable framing/transport faults.
|
|
52
141
|
let response;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// The membership check was causing peers to return redirect responses
|
|
56
|
-
// instead of processing cluster updates, leading to empty promise arrays.
|
|
57
|
-
// TODO: Re-enable and fix cluster membership logic for proper DHT routing
|
|
58
|
-
response = await this.cluster.update(message.record);
|
|
142
|
+
try {
|
|
143
|
+
response = await this.processOperation(message);
|
|
59
144
|
}
|
|
60
|
-
|
|
61
|
-
|
|
145
|
+
catch (err) {
|
|
146
|
+
this.log.error('error processing cluster %s from %p - %e', message.operation, peerId, err);
|
|
147
|
+
response = toClusterErrorEnvelope(err);
|
|
62
148
|
}
|
|
63
149
|
// Encode and yield the response
|
|
64
|
-
|
|
65
|
-
const rec = response;
|
|
66
|
-
this.log('cluster-service:pre-serialize', {
|
|
67
|
-
messageHash: rec?.messageHash,
|
|
68
|
-
responseType: typeof response,
|
|
69
|
-
hasPromises: 'promises' in (rec ?? {}),
|
|
70
|
-
hasCommits: 'commits' in (rec ?? {}),
|
|
71
|
-
promiseKeys: Object.keys(rec?.promises ?? {}),
|
|
72
|
-
commitKeys: Object.keys(rec?.commits ?? {}),
|
|
73
|
-
promiseValues: rec?.promises,
|
|
74
|
-
commitValues: rec?.commits
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
const serialized = JSON.stringify(response);
|
|
78
|
-
if (message.operation === 'update') {
|
|
79
|
-
const deserialized = JSON.parse(serialized);
|
|
80
|
-
this.log('cluster-service:post-serialize', {
|
|
81
|
-
messageHash: deserialized?.messageHash,
|
|
82
|
-
promiseKeys: Object.keys(deserialized?.promises ?? {}),
|
|
83
|
-
commitKeys: Object.keys(deserialized?.commits ?? {})
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
yield new TextEncoder().encode(serialized);
|
|
150
|
+
yield new TextEncoder().encode(JSON.stringify(response));
|
|
87
151
|
}
|
|
88
152
|
};
|
|
89
153
|
void (async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/cluster/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/cluster/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,19 @@ export interface SpreadOnChurnConfig {
|
|
|
15
15
|
departureDebounceMs: number;
|
|
16
16
|
/** Number of peers beyond cluster boundary to target. Default: 4 */
|
|
17
17
|
expansionStep: number;
|
|
18
|
+
/**
|
|
19
|
+
* Per-target dial deadline (ms) for churn pushes. A target that never accepts
|
|
20
|
+
* the connection fails fast instead of stalling the sequential spread pass.
|
|
21
|
+
* Default: 3000 (matches the codebase's other dial caps).
|
|
22
|
+
*/
|
|
23
|
+
pushDialTimeoutMs: number;
|
|
24
|
+
/**
|
|
25
|
+
* Per-target response deadline (ms) for churn pushes. A target that dials OK but
|
|
26
|
+
* never replies (connects, then goes silent) is given up on — the push throws
|
|
27
|
+
* `ResponseTimeoutError`, is recorded as failed, and the loop continues to the
|
|
28
|
+
* next target/block rather than hanging the whole pass. Default: 10000.
|
|
29
|
+
*/
|
|
30
|
+
pushResponseTimeoutMs: number;
|
|
18
31
|
}
|
|
19
32
|
export interface SpreadOnChurnDeps {
|
|
20
33
|
libp2p: Libp2p;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spread-on-churn.d.ts","sourceRoot":"","sources":["../../../src/cluster/spread-on-churn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAShE,MAAM,WAAW,mBAAmB;IACnC,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAA;IAChB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,qBAAqB,EAAE,OAAO,CAAA;IAC9B,kFAAkF;IAClF,eAAe,EAAE,MAAM,CAAA;IACvB,kEAAkE;IAClE,mBAAmB,EAAE,MAAM,CAAA;IAC3B,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"spread-on-churn.d.ts","sourceRoot":"","sources":["../../../src/cluster/spread-on-churn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAShE,MAAM,WAAW,mBAAmB;IACnC,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAA;IAChB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,qBAAqB,EAAE,OAAO,CAAA;IAC9B,kFAAkF;IAClF,eAAe,EAAE,MAAM,CAAA;IACvB,kEAAkE;IAClE,mBAAmB,EAAE,MAAM,CAAA;IAC3B,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,qBAAqB,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,WAAW,CAAA;IACjB,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,IAAI,EAAE,KAAK,CAAA;IACX,WAAW,EAAE,YAAY,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,WAAW;IAC3B,8BAA8B;IAC9B,MAAM,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,EAAE,CAAA;QACjB,SAAS,EAAE,MAAM,EAAE,CAAA;QACnB,MAAM,EAAE,MAAM,EAAE,CAAA;KAChB,CAAC,CAAA;IACF,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;AAiBjD,qBAAa,oBAAqB,YAAW,SAAS;IAYpD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXtB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,mBAAmB,CAAe;IAC1C,OAAO,CAAC,kBAAkB,CAAI;IAE9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAY;gBAG5B,IAAI,EAAE,iBAAiB,EACxC,MAAM,GAAE,OAAO,CAAC,mBAAmB,CAAM;IAQpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAItC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAInC,oBAAoB,IAAI,MAAM;IAI9B,4DAA4D;IACtD,QAAQ,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAM7C,OAAO,CAAC,eAAe;YA2BT,aAAa;IA0G3B,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,SAAS;CASjB"}
|
|
@@ -12,6 +12,8 @@ const DEFAULT_CONFIG = {
|
|
|
12
12
|
healthThreshold: 0.6,
|
|
13
13
|
departureDebounceMs: 5000,
|
|
14
14
|
expansionStep: 4,
|
|
15
|
+
pushDialTimeoutMs: 3000,
|
|
16
|
+
pushResponseTimeoutMs: 10000,
|
|
15
17
|
};
|
|
16
18
|
// ── Monitor ──────────────────────────────────────────────────────────
|
|
17
19
|
export class SpreadOnChurnMonitor {
|
|
@@ -120,10 +122,21 @@ export class SpreadOnChurnMonitor {
|
|
|
120
122
|
const result = await this.deps.repo.get({ blockIds: [blockId] });
|
|
121
123
|
const blockResult = result[blockId];
|
|
122
124
|
if (!blockResult?.block) {
|
|
123
|
-
|
|
125
|
+
// The block has left local storage. No deletion event exists today to evict it
|
|
126
|
+
// from the tracked set, so prune here. Deleting the current element of a Set mid
|
|
127
|
+
// for...of is safe - it does not disturb the rest of the iteration. A later
|
|
128
|
+
// re-commit re-adds the block via the owned-block feed.
|
|
129
|
+
log('no-local-data block=%s (untracking)', blockId);
|
|
130
|
+
this.trackedBlocks.delete(blockId);
|
|
124
131
|
continue;
|
|
125
132
|
}
|
|
126
133
|
const blockData = textEncoder.encode(JSON.stringify(blockResult.block));
|
|
134
|
+
// Carry the source's revision metadata so the replica's `latest` matches the
|
|
135
|
+
// source instead of being fabricated as rev 1 on the receiver.
|
|
136
|
+
const latest = blockResult.state?.latest;
|
|
137
|
+
const blockMeta = latest
|
|
138
|
+
? { [blockId]: { rev: latest.rev, actionId: latest.actionId } }
|
|
139
|
+
: undefined;
|
|
127
140
|
// Push to each target
|
|
128
141
|
const succeeded = [];
|
|
129
142
|
const failed = [];
|
|
@@ -131,9 +144,26 @@ export class SpreadOnChurnMonitor {
|
|
|
131
144
|
try {
|
|
132
145
|
const peerId = peerIdFromString(targetId);
|
|
133
146
|
const client = new BlockTransferClient(peerId, this.deps.peerNetwork, this.deps.protocolPrefix);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
// Bound both the dial and the response read. A target that connects but
|
|
148
|
+
// never replies would otherwise hang this sequential loop forever during
|
|
149
|
+
// churn (exactly when slow/dying peers are common); the deadline makes the
|
|
150
|
+
// push throw, which the catch below records as `failed` so the pass advances.
|
|
151
|
+
const response = await client.pushBlocks([blockId], [blockData], 'replication', blockMeta, {
|
|
152
|
+
dialTimeoutMs: this.config.pushDialTimeoutMs,
|
|
153
|
+
responseTimeoutMs: this.config.pushResponseTimeoutMs,
|
|
154
|
+
});
|
|
155
|
+
// A round-trip that does not throw still does not mean the replica
|
|
156
|
+
// landed: handlePush reports a block in `missing` when it could not
|
|
157
|
+
// parse or persist it. Treat that as a failed push so the resilience
|
|
158
|
+
// mechanism does not falsely count the new owner as holding the block.
|
|
159
|
+
if (response.missing.includes(blockId)) {
|
|
160
|
+
failed.push(targetId);
|
|
161
|
+
log('push:rejected block=%s target=%s (receiver did not persist)', blockId, targetId);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
succeeded.push(targetId);
|
|
165
|
+
log('push:ok block=%s target=%s', blockId, targetId);
|
|
166
|
+
}
|
|
137
167
|
}
|
|
138
168
|
catch (err) {
|
|
139
169
|
failed.push(targetId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spread-on-churn.js","sourceRoot":"","sources":["../../../src/cluster/spread-on-churn.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAElC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C,MAAM,GAAG,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAA;AAC3C,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"spread-on-churn.js","sourceRoot":"","sources":["../../../src/cluster/spread-on-churn.ts"],"names":[],"mappings":"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"}
|