@optimystic/db-core 0.13.5 → 0.14.1
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/structs.d.ts +13 -0
- package/dist/src/cluster/structs.d.ts.map +1 -1
- package/dist/src/cohort-topic/addressing.d.ts +53 -0
- package/dist/src/cohort-topic/addressing.d.ts.map +1 -0
- package/dist/src/cohort-topic/addressing.js +95 -0
- package/dist/src/cohort-topic/addressing.js.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts +105 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js +197 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +50 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +62 -0
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -0
- package/dist/src/cohort-topic/antidos/index.d.ts +6 -0
- package/dist/src/cohort-topic/antidos/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/index.js +6 -0
- package/dist/src/cohort-topic/antidos/index.js.map +1 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.d.ts +95 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.js +146 -0
- package/dist/src/cohort-topic/antidos/rate-limiter.js.map +1 -0
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts +37 -0
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/replay-guard.js +78 -0
- package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -0
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts +50 -0
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -0
- package/dist/src/cohort-topic/antidos/topic-budget.js +95 -0
- package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -0
- package/dist/src/cohort-topic/antiflood/index.d.ts +3 -0
- package/dist/src/cohort-topic/antiflood/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/antiflood/index.js +3 -0
- package/dist/src/cohort-topic/antiflood/index.js.map +1 -0
- package/dist/src/cohort-topic/antiflood/invariants.d.ts +70 -0
- package/dist/src/cohort-topic/antiflood/invariants.d.ts.map +1 -0
- package/dist/src/cohort-topic/antiflood/invariants.js +86 -0
- package/dist/src/cohort-topic/antiflood/invariants.js.map +1 -0
- package/dist/src/cohort-topic/antiflood/jitter.d.ts +65 -0
- package/dist/src/cohort-topic/antiflood/jitter.d.ts.map +1 -0
- package/dist/src/cohort-topic/antiflood/jitter.js +75 -0
- package/dist/src/cohort-topic/antiflood/jitter.js.map +1 -0
- package/dist/src/cohort-topic/coldstart.d.ts +117 -0
- package/dist/src/cohort-topic/coldstart.d.ts.map +1 -0
- package/dist/src/cohort-topic/coldstart.js +127 -0
- package/dist/src/cohort-topic/coldstart.js.map +1 -0
- package/dist/src/cohort-topic/dmax.d.ts +44 -0
- package/dist/src/cohort-topic/dmax.d.ts.map +1 -0
- package/dist/src/cohort-topic/dmax.js +70 -0
- package/dist/src/cohort-topic/dmax.js.map +1 -0
- package/dist/src/cohort-topic/gossip/bus.d.ts +85 -0
- package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/bus.js +146 -0
- package/dist/src/cohort-topic/gossip/bus.js.map +1 -0
- package/dist/src/cohort-topic/gossip/index.d.ts +4 -0
- package/dist/src/cohort-topic/gossip/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/index.js +4 -0
- package/dist/src/cohort-topic/gossip/index.js.map +1 -0
- package/dist/src/cohort-topic/gossip/records.d.ts +12 -0
- package/dist/src/cohort-topic/gossip/records.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/records.js +41 -0
- package/dist/src/cohort-topic/gossip/records.js.map +1 -0
- package/dist/src/cohort-topic/gossip/view.d.ts +39 -0
- package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -0
- package/dist/src/cohort-topic/gossip/view.js +30 -0
- package/dist/src/cohort-topic/gossip/view.js.map +1 -0
- package/dist/src/cohort-topic/index.d.ts +21 -0
- package/dist/src/cohort-topic/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/index.js +21 -0
- package/dist/src/cohort-topic/index.js.map +1 -0
- package/dist/src/cohort-topic/load/barometer.d.ts +78 -0
- package/dist/src/cohort-topic/load/barometer.d.ts.map +1 -0
- package/dist/src/cohort-topic/load/barometer.js +93 -0
- package/dist/src/cohort-topic/load/barometer.js.map +1 -0
- package/dist/src/cohort-topic/load/index.d.ts +2 -0
- package/dist/src/cohort-topic/load/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/load/index.js +2 -0
- package/dist/src/cohort-topic/load/index.js.map +1 -0
- package/dist/src/cohort-topic/member-engine.d.ts +134 -0
- package/dist/src/cohort-topic/member-engine.d.ts.map +1 -0
- package/dist/src/cohort-topic/member-engine.js +264 -0
- package/dist/src/cohort-topic/member-engine.js.map +1 -0
- package/dist/src/cohort-topic/membership/index.d.ts +4 -0
- package/dist/src/cohort-topic/membership/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/index.js +4 -0
- package/dist/src/cohort-topic/membership/index.js.map +1 -0
- package/dist/src/cohort-topic/membership/publisher.d.ts +82 -0
- package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/publisher.js +107 -0
- package/dist/src/cohort-topic/membership/publisher.js.map +1 -0
- package/dist/src/cohort-topic/membership/source.d.ts +31 -0
- package/dist/src/cohort-topic/membership/source.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/source.js +32 -0
- package/dist/src/cohort-topic/membership/source.js.map +1 -0
- package/dist/src/cohort-topic/membership/verifier.d.ts +96 -0
- package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -0
- package/dist/src/cohort-topic/membership/verifier.js +250 -0
- package/dist/src/cohort-topic/membership/verifier.js.map +1 -0
- package/dist/src/cohort-topic/ports.d.ts +149 -0
- package/dist/src/cohort-topic/ports.d.ts.map +1 -0
- package/dist/src/cohort-topic/ports.js +25 -0
- package/dist/src/cohort-topic/ports.js.map +1 -0
- package/dist/src/cohort-topic/promotion.d.ts +135 -0
- package/dist/src/cohort-topic/promotion.d.ts.map +1 -0
- package/dist/src/cohort-topic/promotion.js +269 -0
- package/dist/src/cohort-topic/promotion.js.map +1 -0
- package/dist/src/cohort-topic/registration/bytes.d.ts +16 -0
- package/dist/src/cohort-topic/registration/bytes.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/bytes.js +32 -0
- package/dist/src/cohort-topic/registration/bytes.js.map +1 -0
- package/dist/src/cohort-topic/registration/handoff.d.ts +69 -0
- package/dist/src/cohort-topic/registration/handoff.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/handoff.js +98 -0
- package/dist/src/cohort-topic/registration/handoff.js.map +1 -0
- package/dist/src/cohort-topic/registration/index.d.ts +7 -0
- package/dist/src/cohort-topic/registration/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/index.js +7 -0
- package/dist/src/cohort-topic/registration/index.js.map +1 -0
- package/dist/src/cohort-topic/registration/renewal.d.ts +115 -0
- package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/renewal.js +306 -0
- package/dist/src/cohort-topic/registration/renewal.js.map +1 -0
- package/dist/src/cohort-topic/registration/sharding.d.ts +31 -0
- package/dist/src/cohort-topic/registration/sharding.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/sharding.js +48 -0
- package/dist/src/cohort-topic/registration/sharding.js.map +1 -0
- package/dist/src/cohort-topic/registration/store.d.ts +13 -0
- package/dist/src/cohort-topic/registration/store.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/store.js +72 -0
- package/dist/src/cohort-topic/registration/store.js.map +1 -0
- package/dist/src/cohort-topic/registration/types.d.ts +71 -0
- package/dist/src/cohort-topic/registration/types.d.ts.map +1 -0
- package/dist/src/cohort-topic/registration/types.js +27 -0
- package/dist/src/cohort-topic/registration/types.js.map +1 -0
- package/dist/src/cohort-topic/ring-hash.d.ts +27 -0
- package/dist/src/cohort-topic/ring-hash.d.ts.map +1 -0
- package/dist/src/cohort-topic/ring-hash.js +45 -0
- package/dist/src/cohort-topic/ring-hash.js.map +1 -0
- package/dist/src/cohort-topic/service.d.ts +157 -0
- package/dist/src/cohort-topic/service.d.ts.map +1 -0
- package/dist/src/cohort-topic/service.js +237 -0
- package/dist/src/cohort-topic/service.js.map +1 -0
- package/dist/src/cohort-topic/sig/index.d.ts +3 -0
- package/dist/src/cohort-topic/sig/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/sig/index.js +3 -0
- package/dist/src/cohort-topic/sig/index.js.map +1 -0
- package/dist/src/cohort-topic/sig/payloads.d.ts +25 -0
- package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -0
- package/dist/src/cohort-topic/sig/payloads.js +25 -0
- package/dist/src/cohort-topic/sig/payloads.js.map +1 -0
- package/dist/src/cohort-topic/sig/threshold.d.ts +33 -0
- package/dist/src/cohort-topic/sig/threshold.d.ts.map +1 -0
- package/dist/src/cohort-topic/sig/threshold.js +51 -0
- package/dist/src/cohort-topic/sig/threshold.js.map +1 -0
- package/dist/src/cohort-topic/tiers.d.ts +58 -0
- package/dist/src/cohort-topic/tiers.d.ts.map +1 -0
- package/dist/src/cohort-topic/tiers.js +49 -0
- package/dist/src/cohort-topic/tiers.js.map +1 -0
- package/dist/src/cohort-topic/traffic.d.ts +90 -0
- package/dist/src/cohort-topic/traffic.d.ts.map +1 -0
- package/dist/src/cohort-topic/traffic.js +146 -0
- package/dist/src/cohort-topic/traffic.js.map +1 -0
- package/dist/src/cohort-topic/walk.d.ts +123 -0
- package/dist/src/cohort-topic/walk.d.ts.map +1 -0
- package/dist/src/cohort-topic/walk.js +150 -0
- package/dist/src/cohort-topic/walk.js.map +1 -0
- package/dist/src/cohort-topic/willingness.d.ts +123 -0
- package/dist/src/cohort-topic/willingness.d.ts.map +1 -0
- package/dist/src/cohort-topic/willingness.js +143 -0
- package/dist/src/cohort-topic/willingness.js.map +1 -0
- package/dist/src/cohort-topic/wire/codec.d.ts +42 -0
- package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/codec.js +163 -0
- package/dist/src/cohort-topic/wire/codec.js.map +1 -0
- package/dist/src/cohort-topic/wire/index.d.ts +6 -0
- package/dist/src/cohort-topic/wire/index.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/index.js +6 -0
- package/dist/src/cohort-topic/wire/index.js.map +1 -0
- package/dist/src/cohort-topic/wire/payloads.d.ts +38 -0
- package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/payloads.js +102 -0
- package/dist/src/cohort-topic/wire/payloads.js.map +1 -0
- package/dist/src/cohort-topic/wire/types.d.ts +304 -0
- package/dist/src/cohort-topic/wire/types.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/types.js +16 -0
- package/dist/src/cohort-topic/wire/types.js.map +1 -0
- package/dist/src/cohort-topic/wire/validate.d.ts +25 -0
- package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/validate.js +427 -0
- package/dist/src/cohort-topic/wire/validate.js.map +1 -0
- package/dist/src/collection/collection.d.ts +58 -1
- package/dist/src/collection/collection.d.ts.map +1 -1
- package/dist/src/collection/collection.js +76 -0
- package/dist/src/collection/collection.js.map +1 -1
- package/dist/src/collections/tree/tree.d.ts +58 -2
- package/dist/src/collections/tree/tree.d.ts.map +1 -1
- package/dist/src/collections/tree/tree.js +59 -2
- package/dist/src/collections/tree/tree.js.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/log/log.d.ts +42 -4
- package/dist/src/log/log.d.ts.map +1 -1
- package/dist/src/log/log.js +71 -3
- package/dist/src/log/log.js.map +1 -1
- package/dist/src/log/struct.d.ts +122 -2
- package/dist/src/log/struct.d.ts.map +1 -1
- package/dist/src/log/struct.js.map +1 -1
- package/dist/src/matchmaking/capability-filter.d.ts +24 -0
- package/dist/src/matchmaking/capability-filter.d.ts.map +1 -0
- package/dist/src/matchmaking/capability-filter.js +37 -0
- package/dist/src/matchmaking/capability-filter.js.map +1 -0
- package/dist/src/matchmaking/config.d.ts +77 -0
- package/dist/src/matchmaking/config.d.ts.map +1 -0
- package/dist/src/matchmaking/config.js +69 -0
- package/dist/src/matchmaking/config.js.map +1 -0
- package/dist/src/matchmaking/index.d.ts +21 -0
- package/dist/src/matchmaking/index.d.ts.map +1 -0
- package/dist/src/matchmaking/index.js +21 -0
- package/dist/src/matchmaking/index.js.map +1 -0
- package/dist/src/matchmaking/multi-cohort-seeker.d.ts +133 -0
- package/dist/src/matchmaking/multi-cohort-seeker.d.ts.map +1 -0
- package/dist/src/matchmaking/multi-cohort-seeker.js +142 -0
- package/dist/src/matchmaking/multi-cohort-seeker.js.map +1 -0
- package/dist/src/matchmaking/provider.d.ts +70 -0
- package/dist/src/matchmaking/provider.d.ts.map +1 -0
- package/dist/src/matchmaking/provider.js +93 -0
- package/dist/src/matchmaking/provider.js.map +1 -0
- package/dist/src/matchmaking/query-eval.d.ts +53 -0
- package/dist/src/matchmaking/query-eval.d.ts.map +1 -0
- package/dist/src/matchmaking/query-eval.js +69 -0
- package/dist/src/matchmaking/query-eval.js.map +1 -0
- package/dist/src/matchmaking/seeker-walk.d.ts +95 -0
- package/dist/src/matchmaking/seeker-walk.d.ts.map +1 -0
- package/dist/src/matchmaking/seeker-walk.js +75 -0
- package/dist/src/matchmaking/seeker-walk.js.map +1 -0
- package/dist/src/matchmaking/seeker.d.ts +51 -0
- package/dist/src/matchmaking/seeker.d.ts.map +1 -0
- package/dist/src/matchmaking/seeker.js +62 -0
- package/dist/src/matchmaking/seeker.js.map +1 -0
- package/dist/src/matchmaking/topic-anchor.d.ts +54 -0
- package/dist/src/matchmaking/topic-anchor.d.ts.map +1 -0
- package/dist/src/matchmaking/topic-anchor.js +67 -0
- package/dist/src/matchmaking/topic-anchor.js.map +1 -0
- package/dist/src/matchmaking/voting-quorum.d.ts +233 -0
- package/dist/src/matchmaking/voting-quorum.d.ts.map +1 -0
- package/dist/src/matchmaking/voting-quorum.js +238 -0
- package/dist/src/matchmaking/voting-quorum.js.map +1 -0
- package/dist/src/matchmaking/wire.d.ts +259 -0
- package/dist/src/matchmaking/wire.d.ts.map +1 -0
- package/dist/src/matchmaking/wire.js +484 -0
- package/dist/src/matchmaking/wire.js.map +1 -0
- package/dist/src/network/i-repo.d.ts +9 -0
- package/dist/src/network/i-repo.d.ts.map +1 -1
- package/dist/src/network/repo-protocol.d.ts +3 -1
- package/dist/src/network/repo-protocol.d.ts.map +1 -1
- package/dist/src/network/struct.d.ts +23 -1
- package/dist/src/network/struct.d.ts.map +1 -1
- package/dist/src/reactivity/backfill.d.ts +118 -0
- package/dist/src/reactivity/backfill.d.ts.map +1 -0
- package/dist/src/reactivity/backfill.js +137 -0
- package/dist/src/reactivity/backfill.js.map +1 -0
- package/dist/src/reactivity/backpressure.d.ts +99 -0
- package/dist/src/reactivity/backpressure.d.ts.map +1 -0
- package/dist/src/reactivity/backpressure.js +153 -0
- package/dist/src/reactivity/backpressure.js.map +1 -0
- package/dist/src/reactivity/checkpoint.d.ts +165 -0
- package/dist/src/reactivity/checkpoint.d.ts.map +1 -0
- package/dist/src/reactivity/checkpoint.js +232 -0
- package/dist/src/reactivity/checkpoint.js.map +1 -0
- package/dist/src/reactivity/config.d.ts +131 -0
- package/dist/src/reactivity/config.d.ts.map +1 -0
- package/dist/src/reactivity/config.js +135 -0
- package/dist/src/reactivity/config.js.map +1 -0
- package/dist/src/reactivity/dedupe.d.ts +51 -0
- package/dist/src/reactivity/dedupe.d.ts.map +1 -0
- package/dist/src/reactivity/dedupe.js +90 -0
- package/dist/src/reactivity/dedupe.js.map +1 -0
- package/dist/src/reactivity/forwarder.d.ts +50 -0
- package/dist/src/reactivity/forwarder.d.ts.map +1 -0
- package/dist/src/reactivity/forwarder.js +52 -0
- package/dist/src/reactivity/forwarder.js.map +1 -0
- package/dist/src/reactivity/index.d.ts +34 -0
- package/dist/src/reactivity/index.d.ts.map +1 -0
- package/dist/src/reactivity/index.js +34 -0
- package/dist/src/reactivity/index.js.map +1 -0
- package/dist/src/reactivity/notification.d.ts +72 -0
- package/dist/src/reactivity/notification.d.ts.map +1 -0
- package/dist/src/reactivity/notification.js +92 -0
- package/dist/src/reactivity/notification.js.map +1 -0
- package/dist/src/reactivity/policy.d.ts +59 -0
- package/dist/src/reactivity/policy.d.ts.map +1 -0
- package/dist/src/reactivity/policy.js +64 -0
- package/dist/src/reactivity/policy.js.map +1 -0
- package/dist/src/reactivity/push-state.d.ts +140 -0
- package/dist/src/reactivity/push-state.d.ts.map +1 -0
- package/dist/src/reactivity/push-state.js +270 -0
- package/dist/src/reactivity/push-state.js.map +1 -0
- package/dist/src/reactivity/recover.d.ts +75 -0
- package/dist/src/reactivity/recover.d.ts.map +1 -0
- package/dist/src/reactivity/recover.js +105 -0
- package/dist/src/reactivity/recover.js.map +1 -0
- package/dist/src/reactivity/replay-buffer.d.ts +57 -0
- package/dist/src/reactivity/replay-buffer.d.ts.map +1 -0
- package/dist/src/reactivity/replay-buffer.js +88 -0
- package/dist/src/reactivity/replay-buffer.js.map +1 -0
- package/dist/src/reactivity/resume.d.ts +214 -0
- package/dist/src/reactivity/resume.d.ts.map +1 -0
- package/dist/src/reactivity/resume.js +358 -0
- package/dist/src/reactivity/resume.js.map +1 -0
- package/dist/src/reactivity/rotation.d.ts +283 -0
- package/dist/src/reactivity/rotation.d.ts.map +1 -0
- package/dist/src/reactivity/rotation.js +265 -0
- package/dist/src/reactivity/rotation.js.map +1 -0
- package/dist/src/reactivity/subscriber.d.ts +66 -0
- package/dist/src/reactivity/subscriber.d.ts.map +1 -0
- package/dist/src/reactivity/subscriber.js +75 -0
- package/dist/src/reactivity/subscriber.js.map +1 -0
- package/dist/src/reactivity/subscription.d.ts +52 -0
- package/dist/src/reactivity/subscription.d.ts.map +1 -0
- package/dist/src/reactivity/subscription.js +28 -0
- package/dist/src/reactivity/subscription.js.map +1 -0
- package/dist/src/reactivity/topic-anchor.d.ts +46 -0
- package/dist/src/reactivity/topic-anchor.d.ts.map +1 -0
- package/dist/src/reactivity/topic-anchor.js +61 -0
- package/dist/src/reactivity/topic-anchor.js.map +1 -0
- package/dist/src/reactivity/verify.d.ts +41 -0
- package/dist/src/reactivity/verify.d.ts.map +1 -0
- package/dist/src/reactivity/verify.js +48 -0
- package/dist/src/reactivity/verify.js.map +1 -0
- package/dist/src/reactivity/wire-validate.d.ts +28 -0
- package/dist/src/reactivity/wire-validate.d.ts.map +1 -0
- package/dist/src/reactivity/wire-validate.js +79 -0
- package/dist/src/reactivity/wire-validate.js.map +1 -0
- package/dist/src/reactivity/wire.d.ts +102 -0
- package/dist/src/reactivity/wire.d.ts.map +1 -0
- package/dist/src/reactivity/wire.js +129 -0
- package/dist/src/reactivity/wire.js.map +1 -0
- package/dist/src/transaction/coordinator.d.ts +9 -5
- package/dist/src/transaction/coordinator.d.ts.map +1 -1
- package/dist/src/transaction/coordinator.js +48 -25
- package/dist/src/transaction/coordinator.js.map +1 -1
- package/dist/src/transactor/change-notifier.d.ts +74 -0
- package/dist/src/transactor/change-notifier.d.ts.map +1 -0
- package/dist/src/transactor/change-notifier.js +4 -0
- package/dist/src/transactor/change-notifier.js.map +1 -0
- package/dist/src/transactor/index.d.ts +1 -0
- package/dist/src/transactor/index.d.ts.map +1 -1
- package/dist/src/transactor/index.js +1 -0
- package/dist/src/transactor/index.js.map +1 -1
- package/dist/src/transactor/network-transactor.d.ts +34 -2
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +113 -13
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/package.json +2 -2
- package/src/cluster/structs.ts +13 -0
- package/src/cohort-topic/addressing.ts +112 -0
- package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +253 -0
- package/src/cohort-topic/antidos/bootstrap-evidence.ts +94 -0
- package/src/cohort-topic/antidos/index.ts +5 -0
- package/src/cohort-topic/antidos/rate-limiter.ts +210 -0
- package/src/cohort-topic/antidos/replay-guard.ts +109 -0
- package/src/cohort-topic/antidos/topic-budget.ts +145 -0
- package/src/cohort-topic/antiflood/index.ts +2 -0
- package/src/cohort-topic/antiflood/invariants.ts +108 -0
- package/src/cohort-topic/antiflood/jitter.ts +117 -0
- package/src/cohort-topic/coldstart.ts +204 -0
- package/src/cohort-topic/dmax.ts +88 -0
- package/src/cohort-topic/gossip/bus.ts +217 -0
- package/src/cohort-topic/gossip/index.ts +3 -0
- package/src/cohort-topic/gossip/records.ts +45 -0
- package/src/cohort-topic/gossip/view.ts +66 -0
- package/src/cohort-topic/index.ts +20 -0
- package/src/cohort-topic/load/barometer.ts +134 -0
- package/src/cohort-topic/load/index.ts +1 -0
- package/src/cohort-topic/member-engine.ts +409 -0
- package/src/cohort-topic/membership/index.ts +3 -0
- package/src/cohort-topic/membership/publisher.ts +171 -0
- package/src/cohort-topic/membership/source.ts +41 -0
- package/src/cohort-topic/membership/verifier.ts +345 -0
- package/src/cohort-topic/ports.ts +157 -0
- package/src/cohort-topic/promotion.ts +384 -0
- package/src/cohort-topic/registration/bytes.ts +37 -0
- package/src/cohort-topic/registration/handoff.ts +154 -0
- package/src/cohort-topic/registration/index.ts +6 -0
- package/src/cohort-topic/registration/renewal.ts +423 -0
- package/src/cohort-topic/registration/sharding.ts +61 -0
- package/src/cohort-topic/registration/store.ts +81 -0
- package/src/cohort-topic/registration/types.ts +76 -0
- package/src/cohort-topic/ring-hash.ts +50 -0
- package/src/cohort-topic/service.ts +390 -0
- package/src/cohort-topic/sig/index.ts +2 -0
- package/src/cohort-topic/sig/payloads.ts +39 -0
- package/src/cohort-topic/sig/threshold.ts +64 -0
- package/src/cohort-topic/tiers.ts +74 -0
- package/src/cohort-topic/traffic.ts +211 -0
- package/src/cohort-topic/walk.ts +255 -0
- package/src/cohort-topic/willingness.ts +237 -0
- package/src/cohort-topic/wire/codec.ts +204 -0
- package/src/cohort-topic/wire/index.ts +16 -0
- package/src/cohort-topic/wire/payloads.ts +117 -0
- package/src/cohort-topic/wire/types.ts +343 -0
- package/src/cohort-topic/wire/validate.ts +478 -0
- package/src/collection/collection.ts +93 -1
- package/src/collections/tree/tree.ts +84 -3
- package/src/index.ts +3 -0
- package/src/log/log.ts +86 -7
- package/src/log/struct.ts +126 -2
- package/src/matchmaking/capability-filter.ts +45 -0
- package/src/matchmaking/config.ts +98 -0
- package/src/matchmaking/index.ts +21 -0
- package/src/matchmaking/multi-cohort-seeker.ts +234 -0
- package/src/matchmaking/provider.ts +123 -0
- package/src/matchmaking/query-eval.ts +105 -0
- package/src/matchmaking/seeker-walk.ts +127 -0
- package/src/matchmaking/seeker.ts +86 -0
- package/src/matchmaking/topic-anchor.ts +90 -0
- package/src/matchmaking/voting-quorum.ts +394 -0
- package/src/matchmaking/wire.ts +680 -0
- package/src/network/i-repo.ts +9 -0
- package/src/network/repo-protocol.ts +3 -2
- package/src/network/struct.ts +24 -1
- package/src/reactivity/backfill.ts +220 -0
- package/src/reactivity/backpressure.ts +191 -0
- package/src/reactivity/checkpoint.ts +308 -0
- package/src/reactivity/config.ts +172 -0
- package/src/reactivity/dedupe.ts +132 -0
- package/src/reactivity/forwarder.ts +87 -0
- package/src/reactivity/index.ts +34 -0
- package/src/reactivity/notification.ts +123 -0
- package/src/reactivity/policy.ts +79 -0
- package/src/reactivity/push-state.ts +343 -0
- package/src/reactivity/recover.ts +153 -0
- package/src/reactivity/replay-buffer.ts +141 -0
- package/src/reactivity/resume.ts +549 -0
- package/src/reactivity/rotation.ts +415 -0
- package/src/reactivity/subscriber.ts +132 -0
- package/src/reactivity/subscription.ts +66 -0
- package/src/reactivity/topic-anchor.ts +71 -0
- package/src/reactivity/verify.ts +73 -0
- package/src/reactivity/wire-validate.ts +87 -0
- package/src/reactivity/wire.ts +224 -0
- package/src/transaction/coordinator.ts +53 -27
- package/src/transactor/change-notifier.ts +80 -0
- package/src/transactor/index.ts +1 -0
- package/src/transactor/network-transactor.ts +133 -15
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — participant-side membership verification with a trust-anchor gate.
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/cohort-topic.md` §Membership snapshots and §Bootstrapping trust. A participant verifying
|
|
5
|
+
* a threshold-signed message:
|
|
6
|
+
*
|
|
7
|
+
* 1. takes the message's `signers`, the cohort `coord` the signers should belong to, and the tier;
|
|
8
|
+
* 2. looks up the cached `MembershipCertV1` for that coord (or pulls the source's `current`);
|
|
9
|
+
* 3. checks the signers are a `≥ minSigs` subset of the cert's members and the signature verifies;
|
|
10
|
+
* 4. **on failure against a cached/stale cert, re-fetches the cert from any cohort member exactly
|
|
11
|
+
* once and retries**; still failing → the message is untrusted.
|
|
12
|
+
*
|
|
13
|
+
* **Trust anchoring (the gate this module adds).** Self-consistency (a cert's own threshold signature
|
|
14
|
+
* is a `≥ minSigs` quorum over its own `members`) proves only internal well-formedness — *not* that the
|
|
15
|
+
* attesting key set is the legitimate cohort for the coord. An adversary controlling `k − x` keys could
|
|
16
|
+
* mint a self-consistent cert over a coord it does not own. So before a (re)fetched cert is believed,
|
|
17
|
+
* {@link CachingMembershipVerifier.certIsTrusted} requires it to be self-consistent **AND** anchored by
|
|
18
|
+
* at least one of:
|
|
19
|
+
*
|
|
20
|
+
* - **Trust root** — `(coord, epoch, member-set)` is in the out-of-band-seeded {@link TrustRoot} set
|
|
21
|
+
* (the genesis-block cohorts). Base case of every chain; checked before the direct anchor, so a
|
|
22
|
+
* configured root is authoritative.
|
|
23
|
+
* - **Direct anchor** — the injected {@link IMembershipTrustAnchor} vouches for the binding from a
|
|
24
|
+
* source the node directly trusts (FRET ring agreement / tx-log commit cert, bound in db-p2p). A
|
|
25
|
+
* `"rejected"` verdict is **fatal** (a forgery, even if self-consistent) and overrides the fallback.
|
|
26
|
+
* - **Attestation chain** — a cert carrying a rotation attestation (`prevEpoch`/`rotationSig`/
|
|
27
|
+
* `rotationSigners`) inherits trust when the node already holds a **trusted** predecessor for the same
|
|
28
|
+
* coord at `prevEpoch` whose members form a `≥ minSigs` quorum over this cert's signing payload.
|
|
29
|
+
*
|
|
30
|
+
* **Interim TOFU fallback (documented limit).** For a coord the node cannot anchor (the direct anchor
|
|
31
|
+
* returns `"unknown"` and there is no trust root / chain) and that has **no trusted cert yet**, the
|
|
32
|
+
* verifier falls back to trust-on-first-use of any self-consistent cert — identical to the pre-anchor
|
|
33
|
+
* behavior, so there is strictly no regression on coords no node can verify today (distant T2/T3, and
|
|
34
|
+
* T0/T1 until the committed-index binding lands). Once a coord *does* hold a trusted cert, the chain
|
|
35
|
+
* governs successors: an un-anchored cert for an already-trusted coord is rejected (no TOFU downgrade),
|
|
36
|
+
* which is what gives the rotation chain its teeth. The FRET / tx-log direct-anchor bindings that close
|
|
37
|
+
* the remaining TOFU gap are tracked in `cohort-topic-trust-anchor-fret-binding` and the backlog
|
|
38
|
+
* `...-fret-stabilization-proof` / `...-txlog-committed-binding` tickets.
|
|
39
|
+
*
|
|
40
|
+
* **Deviation from the ticket sketch (documented):** `verifyMessage` takes the cohort `tier`. A coord
|
|
41
|
+
* is an opaque hash, so the T0/T1-vs-T2/T3 source dispatch the same ticket mandates cannot be derived
|
|
42
|
+
* from the coord alone; the caller already knows the tier (it computed the coord from the message's
|
|
43
|
+
* claimed tier/topic). The `tier` is threaded into the gate so the direct anchor is consulted with the
|
|
44
|
+
* same tier the router used (the binding is tier-scoped).
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import type { IMembershipTrustAnchor, RingCoord, TrustRoot } from "../ports.js";
|
|
48
|
+
import { noAuthorityTrustAnchor } from "../ports.js";
|
|
49
|
+
import { b64urlToBytes, bytesToB64url, decodeMembershipCertV1 } from "../wire/codec.js";
|
|
50
|
+
import { CohortWireError } from "../wire/validate.js";
|
|
51
|
+
import type { MembershipCertV1 } from "../wire/types.js";
|
|
52
|
+
import { DEFAULT_MIN_SIGS, type CohortSigner } from "../sig/threshold.js";
|
|
53
|
+
import { membershipCertSigningPayload } from "../sig/payloads.js";
|
|
54
|
+
import type { IMembershipSourceRouter } from "./source.js";
|
|
55
|
+
|
|
56
|
+
/** Outcome of verifying a threshold-signed message against cohort membership. */
|
|
57
|
+
export type VerifyResult = "verified" | "untrusted";
|
|
58
|
+
|
|
59
|
+
/** Caches certs per coord and verifies threshold-signed messages with one stale-cert refetch. */
|
|
60
|
+
export interface MembershipVerifier {
|
|
61
|
+
/** Cache `cert` as the latest known membership for its coord. */
|
|
62
|
+
cache(cert: MembershipCertV1): void;
|
|
63
|
+
/**
|
|
64
|
+
* Verify a threshold-signed message. `expectedCoord` is the cohort the `signers` should belong to;
|
|
65
|
+
* `tier` selects the membership source. Performs the single refetch+retry internally.
|
|
66
|
+
*
|
|
67
|
+
* `opts` bounds the network amplification a flood-exposed caller (the `promote` handler) can suffer:
|
|
68
|
+
* when **both** `minRefetchIntervalMs` and `now` are given, the stale-cert `source.fetch()` retry is
|
|
69
|
+
* **rate-limited per coord** — at most one refetch per coord per interval — so a stream of verify-misses
|
|
70
|
+
* (e.g. forged notices) drives a bounded membership-fetch rate rather than one dial per message.
|
|
71
|
+
* Eventual refetch is *preserved*: a cold cache or a membership rotation still re-fetches once the
|
|
72
|
+
* interval elapses (unlike outright suppression). Omit `opts` (the default, and every existing caller)
|
|
73
|
+
* for the unbounded exactly-one-refetch behavior.
|
|
74
|
+
*/
|
|
75
|
+
verifyMessage(signers: readonly Uint8Array[], expectedCoord: RingCoord, tier: number, payload: Uint8Array, sig: Uint8Array, opts?: RefetchBound): Promise<VerifyResult>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Caller-supplied bound on the membership-cert refetch rate (anti-amplification on a flood-exposed verify
|
|
80
|
+
* path). Both fields are required to take effect; omit either for unbounded refetch.
|
|
81
|
+
*/
|
|
82
|
+
export interface RefetchBound {
|
|
83
|
+
/** Minimum wall-clock gap (ms) between `source.fetch()` refetches for the same coord. */
|
|
84
|
+
readonly minRefetchIntervalMs?: number;
|
|
85
|
+
/** Current wall clock (ms) for the interval comparison. */
|
|
86
|
+
readonly now?: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface MembershipVerifierDeps {
|
|
90
|
+
signer: CohortSigner;
|
|
91
|
+
router: IMembershipSourceRouter;
|
|
92
|
+
minSigs?: number;
|
|
93
|
+
maxMessageBytes?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Direct (base-case) trust anchor for a cert's `coord → keyset` binding. Defaults to
|
|
96
|
+
* {@link noAuthorityTrustAnchor} (every coord `"unknown"`), which preserves the interim TOFU behavior.
|
|
97
|
+
* db-p2p injects the FRET-ring-backed anchor.
|
|
98
|
+
*/
|
|
99
|
+
anchor?: IMembershipTrustAnchor;
|
|
100
|
+
/** Out-of-band-seeded genesis trust roots (the base case of every attestation chain). Defaults to `[]`. */
|
|
101
|
+
trustRoots?: readonly TrustRoot[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Result of the trust gate: accept as a trusted anchor, accept as interim TOFU, or reject outright. */
|
|
105
|
+
type CertTrust = "trusted" | "tofu" | "reject";
|
|
106
|
+
|
|
107
|
+
/** A cached cert and whether it is *trusted* (may serve as an attestation-chain predecessor). */
|
|
108
|
+
interface CachedCert {
|
|
109
|
+
cert: MembershipCertV1;
|
|
110
|
+
/** True only when the cert passed via trust-root / direct-anchor / chain, or was self-published (`cache`). */
|
|
111
|
+
trusted: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A {@link TrustRoot} pre-normalized to the cert's base64url form for cheap matching. */
|
|
115
|
+
interface NormalizedTrustRoot {
|
|
116
|
+
coord: string;
|
|
117
|
+
epoch: string;
|
|
118
|
+
members: ReadonlySet<string>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
class CachingMembershipVerifier implements MembershipVerifier {
|
|
122
|
+
private readonly byCoord = new Map<string, CachedCert>();
|
|
123
|
+
/** Per-coord timestamp of the last `source.fetch()` attempt (the rate-limit clock for {@link RefetchBound}). */
|
|
124
|
+
private readonly lastFetchAt = new Map<string, number>();
|
|
125
|
+
private readonly minSigs: number;
|
|
126
|
+
private readonly anchor: IMembershipTrustAnchor;
|
|
127
|
+
private readonly trustRoots: readonly NormalizedTrustRoot[];
|
|
128
|
+
|
|
129
|
+
constructor(private readonly deps: MembershipVerifierDeps) {
|
|
130
|
+
this.minSigs = deps.minSigs ?? DEFAULT_MIN_SIGS;
|
|
131
|
+
this.anchor = deps.anchor ?? noAuthorityTrustAnchor;
|
|
132
|
+
this.trustRoots = (deps.trustRoots ?? []).map(normalizeTrustRoot);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
cache(cert: MembershipCertV1): void {
|
|
136
|
+
// The public cache feeds this node its OWN freshly-published cert — a node trusts a cert it itself
|
|
137
|
+
// published, so it is marked trusted and may anchor the next rotation in the attestation chain.
|
|
138
|
+
this.byCoord.set(cert.cohortCoord, { cert, trusted: true });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async verifyMessage(signers: readonly Uint8Array[], expectedCoord: RingCoord, tier: number, payload: Uint8Array, sig: Uint8Array, opts?: RefetchBound): Promise<VerifyResult> {
|
|
142
|
+
const coordKey = bytesToB64url(expectedCoord);
|
|
143
|
+
const source = this.deps.router.for(tier);
|
|
144
|
+
|
|
145
|
+
// Seed from the cheap cached view if we hold nothing yet. A cached cert already passed the gate when
|
|
146
|
+
// it was loaded (or was self-published via `cache`), so it is used directly for message verification.
|
|
147
|
+
let cert = this.byCoord.get(coordKey)?.cert;
|
|
148
|
+
if (cert === undefined) {
|
|
149
|
+
cert = await this.loadFrom(source.current(expectedCoord), tier);
|
|
150
|
+
}
|
|
151
|
+
if (cert !== undefined && this.messageVerifies(cert, signers, payload, sig)) {
|
|
152
|
+
return "verified";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Single fetch-and-retry: a stale or missing cert forces a network refresh. A flood-exposed caller
|
|
156
|
+
// (the ungated `promote` handler) bounds the amplification via {@link RefetchBound}: the refetch is
|
|
157
|
+
// then rate-limited to at most one per coord per interval, so a stream of verify-misses (forged
|
|
158
|
+
// notices) cannot turn into a storm of membership dials. Eventual refetch survives — a cold cache or a
|
|
159
|
+
// membership rotation still refreshes once the interval elapses.
|
|
160
|
+
if (!this.refetchAllowed(coordKey, opts)) {
|
|
161
|
+
return "untrusted";
|
|
162
|
+
}
|
|
163
|
+
const refreshed = await this.loadFrom(source.fetch(expectedCoord), tier);
|
|
164
|
+
if (refreshed !== undefined && this.messageVerifies(refreshed, signers, payload, sig)) {
|
|
165
|
+
return "verified";
|
|
166
|
+
}
|
|
167
|
+
return "untrusted";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Whether a `source.fetch()` refetch is permitted for `coordKey` now. Unbounded (always `true`) unless
|
|
172
|
+
* the caller supplies both `minRefetchIntervalMs` and `now`, in which case at most one refetch per coord
|
|
173
|
+
* per interval is allowed — the per-coord fetch-rate bound that caps flood amplification. Records the
|
|
174
|
+
* attempt time when it returns `true` (the dial is about to happen).
|
|
175
|
+
*/
|
|
176
|
+
private refetchAllowed(coordKey: string, opts?: RefetchBound): boolean {
|
|
177
|
+
const minInterval = opts?.minRefetchIntervalMs;
|
|
178
|
+
const now = opts?.now;
|
|
179
|
+
if (minInterval === undefined || now === undefined) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
const last = this.lastFetchAt.get(coordKey);
|
|
183
|
+
if (last !== undefined && now - last < minInterval) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
this.lastFetchAt.set(coordKey, now);
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Decode an encoded cert, run it through the trust gate, cache it (with its trusted status), and return
|
|
192
|
+
* it for message verification; `undefined` if absent, malformed, or rejected by the gate. A rejected
|
|
193
|
+
* cert (failed self-consistency, a `"rejected"` direct anchor, or an un-anchored cert for an
|
|
194
|
+
* already-trusted coord) is treated exactly like an absent cert, so the single refetch still fires.
|
|
195
|
+
*/
|
|
196
|
+
private async loadFrom(pending: Promise<Uint8Array | undefined>, tier: number): Promise<MembershipCertV1 | undefined> {
|
|
197
|
+
const encoded = await pending;
|
|
198
|
+
if (encoded === undefined) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
let cert: MembershipCertV1;
|
|
202
|
+
let trust: CertTrust;
|
|
203
|
+
try {
|
|
204
|
+
cert = decodeMembershipCertV1(encoded, this.deps.maxMessageBytes);
|
|
205
|
+
trust = this.certIsTrusted(cert, tier);
|
|
206
|
+
} catch (err) {
|
|
207
|
+
if (err instanceof CohortWireError) {
|
|
208
|
+
return undefined; // a malformed cert (or non-base64url signer) is treated as no cert
|
|
209
|
+
}
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
if (trust === "reject") {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
// Only a `"trusted"` cert may anchor a successor in the attestation chain; a `"tofu"` cert is cached
|
|
216
|
+
// for message verification but must never launder trust into a rotation.
|
|
217
|
+
this.byCoord.set(cert.cohortCoord, { cert, trusted: trust === "trusted" });
|
|
218
|
+
return cert;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The trust gate (see the module header). A cert is accepted iff it is self-consistent **and** anchored
|
|
223
|
+
* by a trust root, the direct anchor, or the attestation chain; otherwise it falls to the interim TOFU
|
|
224
|
+
* fallback (first-use only). Returns whether the cert is a trusted anchor (`"trusted"`), an interim
|
|
225
|
+
* TOFU acceptance (`"tofu"`), or rejected (`"reject"`).
|
|
226
|
+
*/
|
|
227
|
+
private certIsTrusted(cert: MembershipCertV1, tier: number): CertTrust {
|
|
228
|
+
if (!this.certIsSelfConsistent(cert)) {
|
|
229
|
+
return "reject"; // internal well-formedness is the precondition for any trust path
|
|
230
|
+
}
|
|
231
|
+
if (this.matchesTrustRoot(cert)) {
|
|
232
|
+
return "trusted"; // a configured genesis root is authoritative, checked before the direct anchor
|
|
233
|
+
}
|
|
234
|
+
const verdict = this.anchor.directAnchor(cert, tier);
|
|
235
|
+
if (verdict === "anchored") {
|
|
236
|
+
return "trusted";
|
|
237
|
+
}
|
|
238
|
+
if (verdict === "rejected") {
|
|
239
|
+
return "reject"; // a contradicted binding is a forgery — fatal, overrides the TOFU fallback
|
|
240
|
+
}
|
|
241
|
+
// verdict === "unknown": no local authority for this coord. Try the attestation chain, else fall back.
|
|
242
|
+
if (this.hasRotationAttestation(cert) && this.chainGrantsTrust(cert)) {
|
|
243
|
+
return "trusted";
|
|
244
|
+
}
|
|
245
|
+
return this.fallbackTrust(cert);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The interim fallback for a coord with no trust-root / anchor / chain coverage. Trust-on-first-use:
|
|
250
|
+
* accept a self-consistent cert when the coord holds **no trusted cert yet**, preserving the pre-anchor
|
|
251
|
+
* behavior on coords no node can verify today. But once a coord *is* trust-established, an un-anchored
|
|
252
|
+
* cert (a failed/absent rotation, including a forged rotation off a trusted predecessor) is **rejected**
|
|
253
|
+
* — no silent TOFU downgrade — which is what gives the attestation chain its teeth.
|
|
254
|
+
*/
|
|
255
|
+
private fallbackTrust(cert: MembershipCertV1): CertTrust {
|
|
256
|
+
return this.byCoord.get(cert.cohortCoord)?.trusted ? "reject" : "tofu";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Whether a cert carries a full rotation attestation (all three fields; validated all-or-nothing on the wire). */
|
|
260
|
+
private hasRotationAttestation(cert: MembershipCertV1): boolean {
|
|
261
|
+
return cert.prevEpoch !== undefined && cert.rotationSig !== undefined && cert.rotationSigners !== undefined;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Whether `cert`'s rotation attestation is valid: a **trusted** predecessor for the same coord is cached
|
|
266
|
+
* at `prevEpoch`, the rotation is not self-referential, and the predecessor's members form a `≥ minSigs`
|
|
267
|
+
* quorum over this cert's signing payload via `rotationSig`. A predecessor that only reached the cache via
|
|
268
|
+
* TOFU (not trusted) must not anchor the successor — the trusted-cache invariant.
|
|
269
|
+
*/
|
|
270
|
+
private chainGrantsTrust(cert: MembershipCertV1): boolean {
|
|
271
|
+
const prevEpoch = cert.prevEpoch!;
|
|
272
|
+
if (prevEpoch === cert.cohortEpoch) {
|
|
273
|
+
return false; // a cert cannot rotate from itself
|
|
274
|
+
}
|
|
275
|
+
const predecessor = this.byCoord.get(cert.cohortCoord);
|
|
276
|
+
if (predecessor === undefined || !predecessor.trusted || predecessor.cert.cohortEpoch !== prevEpoch) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
// `rotationSigners` is validated only as a string array, so `b64urlToBytes` may throw `CohortWireError`
|
|
280
|
+
// on a malformed signer — `loadFrom`'s try/catch turns that into "no cert", as for `certIsSelfConsistent`.
|
|
281
|
+
return this.deps.signer.verifyThreshold(
|
|
282
|
+
membershipCertSigningPayload(cert),
|
|
283
|
+
b64urlToBytes(cert.rotationSig!),
|
|
284
|
+
cert.rotationSigners!.map((s) => b64urlToBytes(s)),
|
|
285
|
+
predecessor.cert,
|
|
286
|
+
this.minSigs,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Whether `cert` matches a configured trust root by `(coord, epoch)` and an order-independent member set. */
|
|
291
|
+
private matchesTrustRoot(cert: MembershipCertV1): boolean {
|
|
292
|
+
for (const root of this.trustRoots) {
|
|
293
|
+
if (root.coord === cert.cohortCoord && root.epoch === cert.cohortEpoch && setEqualsArray(root.members, cert.members)) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** A cert is self-consistent only if its own threshold signature is a valid quorum of its members. */
|
|
301
|
+
private certIsSelfConsistent(cert: MembershipCertV1): boolean {
|
|
302
|
+
// `signers` is validated only as a string array (not per-element base64url), so `b64urlToBytes`
|
|
303
|
+
// below may throw `CohortWireError` on a malformed signer — `loadFrom`'s try/catch turns that
|
|
304
|
+
// into "no cert" rather than letting it escape.
|
|
305
|
+
return this.deps.signer.verifyThreshold(
|
|
306
|
+
membershipCertSigningPayload(cert),
|
|
307
|
+
b64urlToBytes(cert.thresholdSig),
|
|
308
|
+
cert.signers.map((s) => b64urlToBytes(s)),
|
|
309
|
+
cert,
|
|
310
|
+
this.minSigs,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private messageVerifies(cert: MembershipCertV1, signers: readonly Uint8Array[], payload: Uint8Array, sig: Uint8Array): boolean {
|
|
315
|
+
return this.deps.signer.verifyThreshold(payload, sig, signers, cert, this.minSigs);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Pre-normalize a {@link TrustRoot} (raw bytes) to the cert's base64url form for cheap matching. */
|
|
320
|
+
function normalizeTrustRoot(root: TrustRoot): NormalizedTrustRoot {
|
|
321
|
+
return {
|
|
322
|
+
coord: bytesToB64url(root.coord),
|
|
323
|
+
epoch: bytesToB64url(root.epoch),
|
|
324
|
+
members: new Set(root.members.map(bytesToB64url)),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Order-independent set equality between a trust-root member set and a cert's member array (duplicate-safe). */
|
|
329
|
+
function setEqualsArray(set: ReadonlySet<string>, members: readonly string[]): boolean {
|
|
330
|
+
const seen = new Set<string>();
|
|
331
|
+
for (const m of members) {
|
|
332
|
+
if (!set.has(m)) {
|
|
333
|
+
return false; // a member not in the root → not a root match
|
|
334
|
+
}
|
|
335
|
+
seen.add(m);
|
|
336
|
+
}
|
|
337
|
+
// Cover both directions: every distinct cert member is in the root AND every root member is covered,
|
|
338
|
+
// so a duplicate-inflated member list cannot masquerade as a full-set match.
|
|
339
|
+
return seen.size === set.size;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Build a participant-side {@link MembershipVerifier}. */
|
|
343
|
+
export function createMembershipVerifier(deps: MembershipVerifierDeps): MembershipVerifier {
|
|
344
|
+
return new CachingMembershipVerifier(deps);
|
|
345
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cohort-topic substrate — transport ports.
|
|
3
|
+
*
|
|
4
|
+
* These are the seams that keep the cohort-topic substrate split cleanly across packages:
|
|
5
|
+
* db-core defines the interfaces here (pure logic depends only on a hash function and
|
|
6
|
+
* byte-array peer IDs), and db-p2p supplies the concrete FRET + libp2p implementations.
|
|
7
|
+
*
|
|
8
|
+
* **db-core never imports FRET or libp2p.** Every db-core substrate module takes these ports
|
|
9
|
+
* by injection (constructor / factory args); db-p2p constructs the FRET-backed versions and
|
|
10
|
+
* composes them with the db-core logic in `CohortTopicService`'s transport host.
|
|
11
|
+
*
|
|
12
|
+
* See `docs/cohort-topic.md` §FRET integration and `tickets` slug
|
|
13
|
+
* `cohort-topic-package-layering` for the authoritative layering rule.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { MembershipCertV1 } from "./wire/types.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Ring coordinate — SHA-256 truncated to the ring width `B` (see {@link IRingHash}). db-core
|
|
20
|
+
* owns this type; db-p2p maps it onto FRET's coordinate type, ensuring the byte representation
|
|
21
|
+
* matches FRET's ring width so routing keys line up on the wire.
|
|
22
|
+
*/
|
|
23
|
+
export type RingCoord = Uint8Array;
|
|
24
|
+
|
|
25
|
+
/** A peer, referenced only by its opaque byte-array id. No multiaddr, no libp2p PeerId here. */
|
|
26
|
+
export interface PeerRef {
|
|
27
|
+
readonly id: Uint8Array;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Wraps FRET's `RouteAndMaybeAct` so the walk / registration decision logic in db-core stays
|
|
32
|
+
* transport-agnostic.
|
|
33
|
+
*/
|
|
34
|
+
export interface ITopicRouter {
|
|
35
|
+
/**
|
|
36
|
+
* Route `activity` to the cohort owning `key` and run the cohort's activity callback,
|
|
37
|
+
* collecting up to `wantK` participants and at least `minSigs` signatures.
|
|
38
|
+
* @returns the encoded cohort reply.
|
|
39
|
+
*/
|
|
40
|
+
routeAndAct(key: RingCoord, activity: Uint8Array, opts: { wantK: number; minSigs: number }): Promise<Uint8Array>;
|
|
41
|
+
/** Direct dial to a cached primary; falls back to {@link routeAndAct} on failure (caller decides). */
|
|
42
|
+
dialMember(member: PeerRef, activity: Uint8Array): Promise<Uint8Array>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Intra-cohort gossip transport (FRET cohort gossip underneath). */
|
|
46
|
+
export interface ICohortGossipTransport {
|
|
47
|
+
/** Fire-and-forget broadcast of `msg` to the cohort at `coord`. */
|
|
48
|
+
broadcast(coord: RingCoord, msg: Uint8Array): void;
|
|
49
|
+
/** Subscribe to inbound cohort gossip; returns an unsubscribe handle. */
|
|
50
|
+
onMessage(handler: (from: PeerRef, msg: Uint8Array) => void): () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Authoritative cohort membership snapshots (FRET `MembershipCertV1` / stabilization underneath). */
|
|
54
|
+
export interface IMembershipSource {
|
|
55
|
+
/** Current cached membership cert for `coord`, encoded; `undefined` if none is cached. */
|
|
56
|
+
current(coord: RingCoord): Promise<Uint8Array | undefined>;
|
|
57
|
+
/** Force one refresh of `coord`'s membership cert (stale-cache retry); encoded, or `undefined`. */
|
|
58
|
+
fetch(coord: RingCoord): Promise<Uint8Array | undefined>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Cohort threshold-signature primitive (FRET's `minSigs = k − x` cohort-signature assembly
|
|
63
|
+
* underneath). db-core defines the seam; db-p2p binds FRET's signer/verifier without modifying it.
|
|
64
|
+
* Peer ids are raw byte arrays here ({@link PeerRef}`.id`), matching the rest of the substrate; the
|
|
65
|
+
* wire layer carries them as base64url strings.
|
|
66
|
+
*/
|
|
67
|
+
export interface ICohortThresholdCrypto {
|
|
68
|
+
/**
|
|
69
|
+
* Assemble a cohort threshold signature over `payload`, collecting at least `minSigs` signers.
|
|
70
|
+
* Resolves to the combined signature and the peers that contributed.
|
|
71
|
+
*/
|
|
72
|
+
assemble(payload: Uint8Array, minSigs: number): Promise<{ thresholdSig: Uint8Array; signers: Uint8Array[] }>;
|
|
73
|
+
/** Verify `thresholdSig` is a valid cohort signature over `payload` produced by `signers`. */
|
|
74
|
+
verify(payload: Uint8Array, thresholdSig: Uint8Array, signers: readonly Uint8Array[]): boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Sink the cohort uses to publish a `MembershipCertV1` (over FRET's `/membership` protocol).
|
|
79
|
+
* db-core's publisher builds + threshold-signs the cert and hands it here; db-p2p serves it.
|
|
80
|
+
*/
|
|
81
|
+
export interface IMembershipPublishSink {
|
|
82
|
+
/** Publish (advertise/serve) an already-signed membership certificate, encoded. */
|
|
83
|
+
publish(encodedCert: Uint8Array): void;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Network-size estimate feeding `d_max`. */
|
|
87
|
+
export interface ISizeEstimator {
|
|
88
|
+
estimate(): { nEst: number; confidence: number };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Verdict on whether a {@link MembershipCertV1}'s `(cohortCoord → members)` binding is vouched by a
|
|
93
|
+
* source the verifying node *directly* trusts.
|
|
94
|
+
*
|
|
95
|
+
* - `"anchored"` — the binding is vouched (e.g. matches FRET `assembleCohort(coord)`, or the
|
|
96
|
+
* tx-log commit certificate). The cert is trusted on this basis.
|
|
97
|
+
* - `"rejected"` — the binding is contradicted (a known-different keyset owns this coord). A
|
|
98
|
+
* `"rejected"` verdict is **fatal**: the cert is a forgery and is rejected even if it is
|
|
99
|
+
* internally self-consistent, overriding the interim TOFU fallback.
|
|
100
|
+
* - `"unknown"` — this node has no local authority over the coord, so it cannot judge. The
|
|
101
|
+
* verifier falls through to the attestation chain / interim TOFU fallback.
|
|
102
|
+
*/
|
|
103
|
+
export type TrustAnchorVerdict = "anchored" | "rejected" | "unknown";
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Direct (base-case) trust anchor for a {@link MembershipCertV1}'s `coord → keyset` binding.
|
|
107
|
+
*
|
|
108
|
+
* db-core owns the chain-of-attestations and trust-root logic; the **direct** anchor is
|
|
109
|
+
* tier/transport-specific (FRET ring agreement for T2/T3, tx-log commit certificate for T0/T1) and
|
|
110
|
+
* is therefore delegated through this port. db-p2p binds it; **db-core never imports FRET**. A node
|
|
111
|
+
* with no local authority for the coord returns `"unknown"` so the verifier falls through to the
|
|
112
|
+
* chain / interim fallback instead of breaking distant verification. See {@link noAuthorityTrustAnchor}
|
|
113
|
+
* for the db-core default (every coord `"unknown"`).
|
|
114
|
+
*/
|
|
115
|
+
export interface IMembershipTrustAnchor {
|
|
116
|
+
/** Judge, from a directly-trusted source, whether `cert.members` is authoritative for `cert.cohortCoord` at `tier`. */
|
|
117
|
+
directAnchor(cert: MembershipCertV1, tier: number): TrustAnchorVerdict;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* An out-of-band-seeded trust root: the genesis-block-related cohorts a joining participant is told
|
|
122
|
+
* to trust (validated against the genesis block hash by the caller before seeding). A trust root is
|
|
123
|
+
* the base case of every attestation chain — a cert matching a root by `(coord, epoch, member-set)`
|
|
124
|
+
* is trusted directly. Byte fields are raw (the verifier compares them against the cert's base64url
|
|
125
|
+
* form internally).
|
|
126
|
+
*/
|
|
127
|
+
export interface TrustRoot {
|
|
128
|
+
/** Cohort coord (32 bytes). */
|
|
129
|
+
readonly coord: Uint8Array;
|
|
130
|
+
/** Cohort epoch (32 bytes) — required so a cert reusing a genesis coord with a swapped keyset is not a root. */
|
|
131
|
+
readonly epoch: Uint8Array;
|
|
132
|
+
/** Authoritative cohort members (any order; matched as a set). */
|
|
133
|
+
readonly members: readonly Uint8Array[];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The db-core default {@link IMembershipTrustAnchor}: it has no local authority over any coord, so it
|
|
138
|
+
* returns `"unknown"` for every cert. Existing verifier callers that inject no anchor get this, keeping
|
|
139
|
+
* the interim TOFU behavior (no regression) until db-p2p binds a real anchor.
|
|
140
|
+
*/
|
|
141
|
+
export const noAuthorityTrustAnchor: IMembershipTrustAnchor = {
|
|
142
|
+
directAnchor(_cert: MembershipCertV1, _tier: number): TrustAnchorVerdict {
|
|
143
|
+
return "unknown";
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Hash + ring math. db-core supplies its OWN SHA-256 (it already hashes logs and block ids);
|
|
149
|
+
* the ring width is configuration. db-p2p, when mapping {@link RingCoord} onto FRET's coordinate
|
|
150
|
+
* type, ensures `ringBits` matches FRET's ring width so coords are byte-compatible.
|
|
151
|
+
*/
|
|
152
|
+
export interface IRingHash {
|
|
153
|
+
/** SHA-256 of `bytes`, truncated to the ring width `B` ({@link ringBits}). */
|
|
154
|
+
H(bytes: Uint8Array): RingCoord;
|
|
155
|
+
/** Ring width in bits. */
|
|
156
|
+
readonly ringBits: number;
|
|
157
|
+
}
|