@optimystic/db-p2p 0.21.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cluster/client.d.ts +10 -0
- package/dist/src/cluster/client.d.ts.map +1 -1
- package/dist/src/cluster/client.js +30 -1
- package/dist/src/cluster/client.js.map +1 -1
- package/dist/src/cluster/cluster-policy.d.ts +13 -2
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js +51 -4
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +42 -17
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +229 -122
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/cluster-size-coupling.d.ts +28 -0
- package/dist/src/cluster/cluster-size-coupling.d.ts.map +1 -0
- package/dist/src/cluster/cluster-size-coupling.js +35 -0
- package/dist/src/cluster/cluster-size-coupling.js.map +1 -0
- package/dist/src/cluster/quorum-restore.d.ts +6 -0
- package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
- package/dist/src/cluster/quorum-restore.js +1 -1
- package/dist/src/cluster/quorum-restore.js.map +1 -1
- package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
- package/dist/src/cluster/reconcile-block.js +15 -3
- package/dist/src/cluster/reconcile-block.js.map +1 -1
- package/dist/src/cluster/service.d.ts +32 -1
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +43 -2
- package/dist/src/cluster/service.js.map +1 -1
- package/dist/src/cohort-topic/stream-util.d.ts +22 -6
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
- package/dist/src/cohort-topic/stream-util.js +56 -10
- package/dist/src/cohort-topic/stream-util.js.map +1 -1
- package/dist/src/dispute/dispute-service.d.ts.map +1 -1
- package/dist/src/dispute/dispute-service.js +9 -3
- package/dist/src/dispute/dispute-service.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +134 -7
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +174 -37
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +3 -2
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +859 -778
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/libp2p-node-rn.d.ts +2 -2
- package/dist/src/libp2p-node-rn.d.ts.map +1 -1
- package/dist/src/libp2p-node-rn.js.map +1 -1
- package/dist/src/libp2p-node.d.ts +2 -2
- package/dist/src/libp2p-node.d.ts.map +1 -1
- package/dist/src/libp2p-node.js.map +1 -1
- package/dist/src/logger.d.ts +17 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js +19 -2
- package/dist/src/logger.js.map +1 -1
- package/dist/src/network/network-manager-service.d.ts +2 -0
- package/dist/src/network/network-manager-service.d.ts.map +1 -1
- package/dist/src/network/network-manager-service.js +4 -0
- package/dist/src/network/network-manager-service.js.map +1 -1
- package/dist/src/optimystic-node.d.ts +35 -0
- package/dist/src/optimystic-node.d.ts.map +1 -0
- package/dist/src/optimystic-node.js +2 -0
- package/dist/src/optimystic-node.js.map +1 -0
- package/dist/src/owned-block-seed.d.ts +6 -3
- package/dist/src/owned-block-seed.d.ts.map +1 -1
- package/dist/src/owned-block-seed.js +16 -3
- package/dist/src/owned-block-seed.js.map +1 -1
- package/dist/src/peer-address-book.d.ts +72 -0
- package/dist/src/peer-address-book.d.ts.map +1 -0
- package/dist/src/peer-address-book.js +123 -0
- package/dist/src/peer-address-book.js.map +1 -0
- package/dist/src/repo/client.d.ts.map +1 -1
- package/dist/src/repo/client.js +11 -2
- package/dist/src/repo/client.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +30 -0
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +95 -3
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +78 -14
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +266 -81
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/rn.d.ts +5 -0
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +5 -0
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +57 -5
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/cached-raw-storage.d.ts +83 -0
- package/dist/src/storage/cached-raw-storage.d.ts.map +1 -0
- package/dist/src/storage/cached-raw-storage.js +152 -0
- package/dist/src/storage/cached-raw-storage.js.map +1 -0
- package/dist/src/storage/cached-store-driver.d.ts +186 -0
- package/dist/src/storage/cached-store-driver.d.ts.map +1 -0
- package/dist/src/storage/cached-store-driver.js +775 -0
- package/dist/src/storage/cached-store-driver.js.map +1 -0
- package/dist/src/storage/i-block-storage.d.ts +20 -1
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-raw-storage.d.ts +12 -5
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.d.ts +234 -0
- package/dist/src/storage/shared-cache-pool.d.ts.map +1 -0
- package/dist/src/storage/shared-cache-pool.js +354 -0
- package/dist/src/storage/shared-cache-pool.js.map +1 -0
- package/dist/src/storage/storage-repo.d.ts +56 -3
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +124 -18
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/testing/raw-storage-conformance.d.ts +2 -1
- package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
- package/dist/src/testing/raw-storage-conformance.js +52 -2
- package/dist/src/testing/raw-storage-conformance.js.map +1 -1
- package/package.json +3 -3
- package/readme.md +668 -653
- package/src/cluster/block-transfer.ts +424 -424
- package/src/cluster/client.ts +119 -88
- package/src/cluster/cluster-error.ts +64 -64
- package/src/cluster/cluster-policy.ts +203 -152
- package/src/cluster/cluster-repo.ts +245 -125
- package/src/cluster/cluster-size-coupling.ts +45 -0
- package/src/cluster/commit-cert.ts +139 -139
- package/src/cluster/i-transaction-state-store.ts +43 -43
- package/src/cluster/memory-transaction-state-store.ts +56 -56
- package/src/cluster/peer-key-binding.ts +37 -37
- package/src/cluster/persistent-transaction-state-store.ts +92 -92
- package/src/cluster/quorum-restore.ts +223 -223
- package/src/cluster/reconcile-block.ts +203 -191
- package/src/cluster/service.ts +293 -241
- package/src/cluster/supermajority-coupling.ts +37 -37
- package/src/cohort-topic/bootstrap-evidence-builder.ts +122 -122
- package/src/cohort-topic/bootstrap-evidence-verifiers.ts +132 -132
- package/src/cohort-topic/bootstrap-parent-reference.ts +159 -159
- package/src/cohort-topic/change-bridge.ts +109 -109
- package/src/cohort-topic/cohort-gossip-driver.ts +231 -231
- package/src/cohort-topic/cohort-gossip-transport.ts +84 -84
- package/src/cohort-topic/fret-trust-anchor.ts +153 -153
- package/src/cohort-topic/host.ts +2901 -2901
- package/src/cohort-topic/index.ts +13 -13
- package/src/cohort-topic/membership-publish-sink.ts +20 -20
- package/src/cohort-topic/membership-source.ts +68 -68
- package/src/cohort-topic/peer-codec.ts +31 -31
- package/src/cohort-topic/peer-sig.ts +86 -86
- package/src/cohort-topic/protocols.ts +71 -71
- package/src/cohort-topic/reactivity-membership-gate.ts +77 -77
- package/src/cohort-topic/size-estimator.ts +16 -16
- package/src/cohort-topic/stream-util.ts +135 -87
- package/src/cohort-topic/threshold-crypto.ts +239 -239
- package/src/cohort-topic/topic-router.ts +77 -77
- package/src/dispute/arbitrator-selection.ts +138 -138
- package/src/dispute/cascade.ts +524 -524
- package/src/dispute/dispute-service.ts +11 -5
- package/src/dispute/invalidation.ts +625 -625
- package/src/inbound-authorization.ts +190 -190
- package/src/index.ts +52 -47
- package/src/libp2p-key-network.ts +1120 -958
- package/src/libp2p-node-base.ts +1675 -1591
- package/src/libp2p-node-rn.ts +30 -30
- package/src/libp2p-node.ts +36 -36
- package/src/logger.ts +19 -2
- package/src/matchmaking/aggregate-counts.ts +104 -104
- package/src/matchmaking/index.ts +20 -20
- package/src/matchmaking/module.ts +363 -363
- package/src/matchmaking/protocols.ts +51 -51
- package/src/matchmaking/provider-manager.ts +95 -95
- package/src/matchmaking/query-handler.ts +88 -88
- package/src/matchmaking/query-transport.ts +492 -492
- package/src/matchmaking/seeker-manager.ts +64 -64
- package/src/matchmaking/seeker-walk-client.ts +293 -293
- package/src/matchmaking/traffic-validation.ts +195 -195
- package/src/network/network-manager-service.ts +5 -0
- package/src/optimystic-node.ts +36 -0
- package/src/owned-block-seed.ts +53 -40
- package/src/peer-address-book.ts +149 -0
- package/src/protocol-limits.ts +33 -33
- package/src/reactivity/forwarder-host.ts +438 -438
- package/src/reactivity/index.ts +19 -19
- package/src/reactivity/notify-transport.ts +144 -144
- package/src/reactivity/origination-manager.ts +192 -192
- package/src/reactivity/protocols.ts +61 -61
- package/src/reactivity/push-state-gossip.ts +291 -291
- package/src/reactivity/recover-transport.ts +408 -408
- package/src/reactivity/rotation-rereg-scheduler.ts +256 -256
- package/src/reactivity/subscriber-registry.ts +96 -96
- package/src/reactivity/subscription-manager.ts +450 -450
- package/src/reactivity/topic-bytes.ts +37 -37
- package/src/repo/client.ts +12 -2
- package/src/repo/cluster-coordinator.ts +99 -3
- package/src/repo/coordinator-repo.ts +305 -82
- package/src/repo/types.ts +7 -7
- package/src/rn.ts +39 -34
- package/src/rpc-deadline.ts +45 -45
- package/src/storage/arachnode-partition.ts +74 -74
- package/src/storage/block-storage.ts +59 -6
- package/src/storage/cached-raw-storage.ts +180 -0
- package/src/storage/cached-store-driver.ts +859 -0
- package/src/storage/i-block-storage.ts +20 -1
- package/src/storage/i-kv-store.ts +8 -8
- package/src/storage/i-raw-storage.ts +12 -5
- package/src/storage/kv-raw-storage.ts +135 -135
- package/src/storage/memory-kv-store.ts +28 -28
- package/src/storage/memory-storage.ts +25 -25
- package/src/storage/memory-store-driver.ts +157 -157
- package/src/storage/raw-store-codec.ts +42 -42
- package/src/storage/raw-store-driver.ts +80 -80
- package/src/storage/ring-selector.ts +317 -317
- package/src/storage/ring-shift-coordinator.ts +271 -271
- package/src/storage/shared-cache-pool.ts +452 -0
- package/src/storage/storage-repo.ts +1014 -903
- package/src/testing/cohort-topic-mesh-harness.ts +663 -663
- package/src/testing/index.ts +8 -8
- package/src/testing/matchmaking-mesh-harness.ts +475 -475
- package/src/testing/raw-storage-conformance.ts +453 -397
- package/src/testing/reactivity-mesh-harness.ts +922 -922
- package/dist/src/storage/restoration-coordinator-v2.d.ts +0 -67
- package/dist/src/storage/restoration-coordinator-v2.d.ts.map +0 -1
- package/dist/src/storage/restoration-coordinator-v2.js +0 -172
- package/dist/src/storage/restoration-coordinator-v2.js.map +0 -1
package/src/libp2p-node-rn.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type
|
|
5
|
-
type
|
|
6
|
-
|
|
7
|
-
} from './
|
|
8
|
-
|
|
9
|
-
export type { Libp2pTransports, NodeOptions, RawStorageProvider };
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* React Native-friendly libp2p node factory.
|
|
13
|
-
*
|
|
14
|
-
* This entrypoint intentionally does not import Node-only transports (like `@libp2p/tcp`).
|
|
15
|
-
* Callers must provide `options.transports` (and typically `options.listenAddrs`).
|
|
16
|
-
*/
|
|
17
|
-
export async function createLibp2pNode(options: NodeOptions): Promise<
|
|
18
|
-
const transports = options.transports;
|
|
19
|
-
if (!transports || transports.length === 0) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
'createLibp2pNode (RN) requires options.transports. ' +
|
|
22
|
-
'Provide an RN-compatible transport (e.g. WebSockets) and any required listenAddrs.'
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return await createLibp2pNodeBase(options, {
|
|
27
|
-
listenAddrs: [],
|
|
28
|
-
transports,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
createLibp2pNodeBase,
|
|
3
|
+
type Libp2pTransports,
|
|
4
|
+
type NodeOptions,
|
|
5
|
+
type RawStorageProvider,
|
|
6
|
+
} from './libp2p-node-base.js';
|
|
7
|
+
import type { OptimysticNode } from './optimystic-node.js';
|
|
8
|
+
|
|
9
|
+
export type { Libp2pTransports, NodeOptions, RawStorageProvider };
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* React Native-friendly libp2p node factory.
|
|
13
|
+
*
|
|
14
|
+
* This entrypoint intentionally does not import Node-only transports (like `@libp2p/tcp`).
|
|
15
|
+
* Callers must provide `options.transports` (and typically `options.listenAddrs`).
|
|
16
|
+
*/
|
|
17
|
+
export async function createLibp2pNode(options: NodeOptions): Promise<OptimysticNode> {
|
|
18
|
+
const transports = options.transports;
|
|
19
|
+
if (!transports || transports.length === 0) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'createLibp2pNode (RN) requires options.transports. ' +
|
|
22
|
+
'Provide an RN-compatible transport (e.g. WebSockets) and any required listenAddrs.'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return await createLibp2pNodeBase(options, {
|
|
27
|
+
listenAddrs: [],
|
|
28
|
+
transports,
|
|
29
|
+
});
|
|
30
|
+
}
|
package/src/libp2p-node.ts
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
} from './
|
|
11
|
-
|
|
12
|
-
export type { Libp2pTransports, NodeOptions, RawStorageProvider };
|
|
13
|
-
|
|
14
|
-
export async function createLibp2pNode(options: NodeOptions): Promise<
|
|
15
|
-
const port = options.port ?? 0;
|
|
16
|
-
const wsHost = options.wsHost ?? '0.0.0.0';
|
|
17
|
-
|
|
18
|
-
const defaultTransports: Libp2pTransports = [];
|
|
19
|
-
const defaultListenAddrs: string[] = [];
|
|
20
|
-
|
|
21
|
-
if (!options.disableTcp) {
|
|
22
|
-
defaultTransports.push(tcp());
|
|
23
|
-
defaultListenAddrs.push(`/ip4/0.0.0.0/tcp/${port}`);
|
|
24
|
-
}
|
|
25
|
-
if (options.wsPort !== undefined) {
|
|
26
|
-
defaultTransports.push(webSockets());
|
|
27
|
-
defaultListenAddrs.push(`/ip4/${wsHost}/tcp/${options.wsPort}/ws`);
|
|
28
|
-
}
|
|
29
|
-
// Always include the relay transport so this node can dial through relays
|
|
30
|
-
defaultTransports.push(circuitRelayTransport());
|
|
31
|
-
|
|
32
|
-
return await createLibp2pNodeBase(options, {
|
|
33
|
-
listenAddrs: defaultListenAddrs,
|
|
34
|
-
transports: defaultTransports,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
1
|
+
import { tcp } from '@libp2p/tcp';
|
|
2
|
+
import { webSockets } from '@libp2p/websockets';
|
|
3
|
+
import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
|
|
4
|
+
import {
|
|
5
|
+
createLibp2pNodeBase,
|
|
6
|
+
type Libp2pTransports,
|
|
7
|
+
type NodeOptions,
|
|
8
|
+
type RawStorageProvider,
|
|
9
|
+
} from './libp2p-node-base.js';
|
|
10
|
+
import type { OptimysticNode } from './optimystic-node.js';
|
|
11
|
+
|
|
12
|
+
export type { Libp2pTransports, NodeOptions, RawStorageProvider };
|
|
13
|
+
|
|
14
|
+
export async function createLibp2pNode(options: NodeOptions): Promise<OptimysticNode> {
|
|
15
|
+
const port = options.port ?? 0;
|
|
16
|
+
const wsHost = options.wsHost ?? '0.0.0.0';
|
|
17
|
+
|
|
18
|
+
const defaultTransports: Libp2pTransports = [];
|
|
19
|
+
const defaultListenAddrs: string[] = [];
|
|
20
|
+
|
|
21
|
+
if (!options.disableTcp) {
|
|
22
|
+
defaultTransports.push(tcp());
|
|
23
|
+
defaultListenAddrs.push(`/ip4/0.0.0.0/tcp/${port}`);
|
|
24
|
+
}
|
|
25
|
+
if (options.wsPort !== undefined) {
|
|
26
|
+
defaultTransports.push(webSockets());
|
|
27
|
+
defaultListenAddrs.push(`/ip4/${wsHost}/tcp/${options.wsPort}/ws`);
|
|
28
|
+
}
|
|
29
|
+
// Always include the relay transport so this node can dial through relays
|
|
30
|
+
defaultTransports.push(circuitRelayTransport());
|
|
31
|
+
|
|
32
|
+
return await createLibp2pNodeBase(options, {
|
|
33
|
+
listenAddrs: defaultListenAddrs,
|
|
34
|
+
transports: defaultTransports,
|
|
35
|
+
});
|
|
36
|
+
}
|
package/src/logger.ts
CHANGED
|
@@ -2,8 +2,25 @@ import debug from 'debug'
|
|
|
2
2
|
|
|
3
3
|
const BASE_NAMESPACE = 'optimystic:db-p2p'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Build a `debug` logger under `optimystic:db-p2p:<subNamespace>`, optionally suffixed with the
|
|
7
|
+
* owning node's peer id (`:<first 12 chars>`) so lines from several nodes sharing one process —
|
|
8
|
+
* every integration test — are attributable. Omit `peerId` and the namespace is byte-for-byte the
|
|
9
|
+
* un-suffixed one, so callers that don't know their peer id are unaffected.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: 12 chars matches the truncation the rest of this package already uses in log payloads, but
|
|
12
|
+
* every Ed25519 peer id starts with the constant `12D3KooW`, so only ~4 base58 characters actually
|
|
13
|
+
* distinguish nodes (~11M combinations — ample for the handful of nodes a test process runs).
|
|
14
|
+
* Widen this only if a run ever needs to match a namespace against a full peer id.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: only the two classes the diagnosability ticket named pass a peer id today
|
|
17
|
+
* (`Libp2pKeyPeerNetwork`, `CoordinatorRepo`); the package's other ~30 `createLogger` call sites
|
|
18
|
+
* still log under a flat namespace. Thread a peer id through any of them if a future diagnosis
|
|
19
|
+
* needs per-node attribution from that subsystem — the mechanism is already here.
|
|
20
|
+
*/
|
|
21
|
+
export function createLogger(subNamespace: string, peerId?: string): debug.Debugger {
|
|
22
|
+
const suffix = peerId ? `:${peerId.substring(0, 12)}` : ''
|
|
23
|
+
return debug(`${BASE_NAMESPACE}:${subNamespace}${suffix}`)
|
|
7
24
|
}
|
|
8
25
|
|
|
9
26
|
export const verbose = typeof process !== 'undefined'
|
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Matchmaking — root-cohort aggregate-count producer (db-p2p, wires the substrate to the sweep summary).
|
|
3
|
-
*
|
|
4
|
-
* `docs/matchmaking.md` §Multi-cohort sweep / §Aggregated provider counts. A *promoted* topic's root
|
|
5
|
-
* cohort answers a sweeping seeker with an {@link AggregateCountV1}: log-bucketed provider counts per
|
|
6
|
-
* tier-1 prefix shard, **threshold-signed** (an attested *registered*-provider count — unlike the
|
|
7
|
-
* advisory, single-member-signed `QueryReplyV1`). This module is the thin db-p2p binding that builds
|
|
8
|
-
* that message from the root's per-shard accounting and the cohort threshold signer.
|
|
9
|
-
*
|
|
10
|
-
* Two invariants the doc mandates, enforced here:
|
|
11
|
-
*
|
|
12
|
-
* - **Depth gate.** The aggregate is produced **only** when the tree depth at this cohort is
|
|
13
|
-
* `>= aggregate_count_minimum_tier` (default 1). A cold cohort that fell through to `NoState` has no
|
|
14
|
-
* tier-1 children to summarize, so {@link buildAggregateCount} returns `undefined` and the seeker
|
|
15
|
-
* falls back to the single-cohort sample.
|
|
16
|
-
* - **Log-bucketing.** Raw per-shard counts are quantized through the db-core {@link logBucketCount}
|
|
17
|
-
* (largest power of two `<= n`), never forwarded exactly.
|
|
18
|
-
*
|
|
19
|
-
* Like the cohort query handler, the inputs are injected (records/accounting, epoch, depth, the threshold
|
|
20
|
-
* signer), so this unit-tests without a live FRET/libp2p stack — the mock-tier e2e that drives it from a
|
|
21
|
-
* real promoted root is a documented follow-on. In the FRET host the root `CoordEngine` supplies
|
|
22
|
-
* `shardCounts` from its child-cohort accounting (the per-tier-1 `childCohortCount` / gossip-derived
|
|
23
|
-
* summaries), `treeDepth` from its tier state, `cohortEpoch` from the membership, and `thresholdSign`
|
|
24
|
-
* bound to the cohort signer's `/sign` assembly.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
bytesToB64url,
|
|
29
|
-
logBucketCount,
|
|
30
|
-
aggregateCountSigningPayload,
|
|
31
|
-
AGGREGATE_COUNT_MINIMUM_TIER,
|
|
32
|
-
DEFAULT_FANOUT,
|
|
33
|
-
DEFAULT_SWEEP_TARGET_TIER,
|
|
34
|
-
type AggregateBucketV1,
|
|
35
|
-
type AggregateCountV1,
|
|
36
|
-
} from "@optimystic/db-core";
|
|
37
|
-
|
|
38
|
-
/** Everything {@link buildAggregateCount} needs from the root cohort, all injected. */
|
|
39
|
-
export interface AggregateCountContext {
|
|
40
|
-
/** The topic id being summarized, 32 bytes. */
|
|
41
|
-
readonly topicId: Uint8Array;
|
|
42
|
-
/** The current cohort epoch, 32 bytes. */
|
|
43
|
-
readonly cohortEpoch: Uint8Array;
|
|
44
|
-
/**
|
|
45
|
-
* Tree depth at this cohort. The aggregate is produced only when `treeDepth >= minimumTier`; a cold
|
|
46
|
-
* cohort (depth 0) summarizes nothing.
|
|
47
|
-
*/
|
|
48
|
-
readonly treeDepth: number;
|
|
49
|
-
/**
|
|
50
|
-
* Raw per-tier-1-shard provider counts, keyed by `prefixSlot` (`0..fanout-1`). Slots absent from the
|
|
51
|
-
* map count as `0`. The root derives these from its child-cohort accounting; this binding log-buckets
|
|
52
|
-
* them and never trusts the caller to pre-bucket.
|
|
53
|
-
*/
|
|
54
|
-
readonly shardCounts: ReadonlyMap<number, number>;
|
|
55
|
-
/**
|
|
56
|
-
* Assemble a cohort threshold signature over the canonical aggregate image (db-p2p binds the cohort
|
|
57
|
-
* signer). Returns the multisig blob plus the `>= minSigs` signer subset that produced it.
|
|
58
|
-
*/
|
|
59
|
-
readonly thresholdSign: (payload: Uint8Array) => Promise<{ thresholdSig: Uint8Array; signers: Uint8Array[] }>;
|
|
60
|
-
/** The tier whose shards are summarized. Default {@link DEFAULT_SWEEP_TARGET_TIER} (1). */
|
|
61
|
-
readonly targetTier?: number;
|
|
62
|
-
/** Number of prefix slots `F`. Default {@link DEFAULT_FANOUT} (16). */
|
|
63
|
-
readonly fanout?: number;
|
|
64
|
-
/** Depth gate. Default {@link AGGREGATE_COUNT_MINIMUM_TIER} (1). */
|
|
65
|
-
readonly minimumTier?: number;
|
|
66
|
-
/** Emit zero-count shards too (default `false` — empty shards are omitted to keep the summary compact). */
|
|
67
|
-
readonly includeEmptyShards?: boolean;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Build the threshold-signed {@link AggregateCountV1} for the root's per-shard accounting, or `undefined`
|
|
72
|
-
* when the depth gate is not met (`treeDepth < minimumTier`). Counts are log-bucketed; the canonical
|
|
73
|
-
* image is order-independent (the signing payload sorts buckets), so signer and verifier agree.
|
|
74
|
-
*/
|
|
75
|
-
export async function buildAggregateCount(ctx: AggregateCountContext): Promise<AggregateCountV1 | undefined> {
|
|
76
|
-
const minimumTier = ctx.minimumTier ?? AGGREGATE_COUNT_MINIMUM_TIER;
|
|
77
|
-
if (ctx.treeDepth < minimumTier) {
|
|
78
|
-
// Cold / unpromoted root: no tier-1 children to summarize (matches the seeker's NoState fallback).
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const targetTier = ctx.targetTier ?? DEFAULT_SWEEP_TARGET_TIER;
|
|
83
|
-
const fanout = ctx.fanout ?? DEFAULT_FANOUT;
|
|
84
|
-
const bucketCounts: AggregateBucketV1[] = [];
|
|
85
|
-
for (let prefixSlot = 0; prefixSlot < fanout; prefixSlot++) {
|
|
86
|
-
const count = logBucketCount(ctx.shardCounts.get(prefixSlot) ?? 0);
|
|
87
|
-
if (count > 0 || ctx.includeEmptyShards === true) {
|
|
88
|
-
bucketCounts.push({ targetTier, prefixSlot, count });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const unsigned: Omit<AggregateCountV1, "signature" | "signers"> = {
|
|
93
|
-
v: 1,
|
|
94
|
-
topicId: bytesToB64url(ctx.topicId),
|
|
95
|
-
bucketCounts,
|
|
96
|
-
cohortEpoch: bytesToB64url(ctx.cohortEpoch),
|
|
97
|
-
};
|
|
98
|
-
const { thresholdSig, signers } = await ctx.thresholdSign(aggregateCountSigningPayload(unsigned));
|
|
99
|
-
return {
|
|
100
|
-
...unsigned,
|
|
101
|
-
signature: bytesToB64url(thresholdSig),
|
|
102
|
-
signers: signers.map(bytesToB64url),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — root-cohort aggregate-count producer (db-p2p, wires the substrate to the sweep summary).
|
|
3
|
+
*
|
|
4
|
+
* `docs/matchmaking.md` §Multi-cohort sweep / §Aggregated provider counts. A *promoted* topic's root
|
|
5
|
+
* cohort answers a sweeping seeker with an {@link AggregateCountV1}: log-bucketed provider counts per
|
|
6
|
+
* tier-1 prefix shard, **threshold-signed** (an attested *registered*-provider count — unlike the
|
|
7
|
+
* advisory, single-member-signed `QueryReplyV1`). This module is the thin db-p2p binding that builds
|
|
8
|
+
* that message from the root's per-shard accounting and the cohort threshold signer.
|
|
9
|
+
*
|
|
10
|
+
* Two invariants the doc mandates, enforced here:
|
|
11
|
+
*
|
|
12
|
+
* - **Depth gate.** The aggregate is produced **only** when the tree depth at this cohort is
|
|
13
|
+
* `>= aggregate_count_minimum_tier` (default 1). A cold cohort that fell through to `NoState` has no
|
|
14
|
+
* tier-1 children to summarize, so {@link buildAggregateCount} returns `undefined` and the seeker
|
|
15
|
+
* falls back to the single-cohort sample.
|
|
16
|
+
* - **Log-bucketing.** Raw per-shard counts are quantized through the db-core {@link logBucketCount}
|
|
17
|
+
* (largest power of two `<= n`), never forwarded exactly.
|
|
18
|
+
*
|
|
19
|
+
* Like the cohort query handler, the inputs are injected (records/accounting, epoch, depth, the threshold
|
|
20
|
+
* signer), so this unit-tests without a live FRET/libp2p stack — the mock-tier e2e that drives it from a
|
|
21
|
+
* real promoted root is a documented follow-on. In the FRET host the root `CoordEngine` supplies
|
|
22
|
+
* `shardCounts` from its child-cohort accounting (the per-tier-1 `childCohortCount` / gossip-derived
|
|
23
|
+
* summaries), `treeDepth` from its tier state, `cohortEpoch` from the membership, and `thresholdSign`
|
|
24
|
+
* bound to the cohort signer's `/sign` assembly.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
bytesToB64url,
|
|
29
|
+
logBucketCount,
|
|
30
|
+
aggregateCountSigningPayload,
|
|
31
|
+
AGGREGATE_COUNT_MINIMUM_TIER,
|
|
32
|
+
DEFAULT_FANOUT,
|
|
33
|
+
DEFAULT_SWEEP_TARGET_TIER,
|
|
34
|
+
type AggregateBucketV1,
|
|
35
|
+
type AggregateCountV1,
|
|
36
|
+
} from "@optimystic/db-core";
|
|
37
|
+
|
|
38
|
+
/** Everything {@link buildAggregateCount} needs from the root cohort, all injected. */
|
|
39
|
+
export interface AggregateCountContext {
|
|
40
|
+
/** The topic id being summarized, 32 bytes. */
|
|
41
|
+
readonly topicId: Uint8Array;
|
|
42
|
+
/** The current cohort epoch, 32 bytes. */
|
|
43
|
+
readonly cohortEpoch: Uint8Array;
|
|
44
|
+
/**
|
|
45
|
+
* Tree depth at this cohort. The aggregate is produced only when `treeDepth >= minimumTier`; a cold
|
|
46
|
+
* cohort (depth 0) summarizes nothing.
|
|
47
|
+
*/
|
|
48
|
+
readonly treeDepth: number;
|
|
49
|
+
/**
|
|
50
|
+
* Raw per-tier-1-shard provider counts, keyed by `prefixSlot` (`0..fanout-1`). Slots absent from the
|
|
51
|
+
* map count as `0`. The root derives these from its child-cohort accounting; this binding log-buckets
|
|
52
|
+
* them and never trusts the caller to pre-bucket.
|
|
53
|
+
*/
|
|
54
|
+
readonly shardCounts: ReadonlyMap<number, number>;
|
|
55
|
+
/**
|
|
56
|
+
* Assemble a cohort threshold signature over the canonical aggregate image (db-p2p binds the cohort
|
|
57
|
+
* signer). Returns the multisig blob plus the `>= minSigs` signer subset that produced it.
|
|
58
|
+
*/
|
|
59
|
+
readonly thresholdSign: (payload: Uint8Array) => Promise<{ thresholdSig: Uint8Array; signers: Uint8Array[] }>;
|
|
60
|
+
/** The tier whose shards are summarized. Default {@link DEFAULT_SWEEP_TARGET_TIER} (1). */
|
|
61
|
+
readonly targetTier?: number;
|
|
62
|
+
/** Number of prefix slots `F`. Default {@link DEFAULT_FANOUT} (16). */
|
|
63
|
+
readonly fanout?: number;
|
|
64
|
+
/** Depth gate. Default {@link AGGREGATE_COUNT_MINIMUM_TIER} (1). */
|
|
65
|
+
readonly minimumTier?: number;
|
|
66
|
+
/** Emit zero-count shards too (default `false` — empty shards are omitted to keep the summary compact). */
|
|
67
|
+
readonly includeEmptyShards?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Build the threshold-signed {@link AggregateCountV1} for the root's per-shard accounting, or `undefined`
|
|
72
|
+
* when the depth gate is not met (`treeDepth < minimumTier`). Counts are log-bucketed; the canonical
|
|
73
|
+
* image is order-independent (the signing payload sorts buckets), so signer and verifier agree.
|
|
74
|
+
*/
|
|
75
|
+
export async function buildAggregateCount(ctx: AggregateCountContext): Promise<AggregateCountV1 | undefined> {
|
|
76
|
+
const minimumTier = ctx.minimumTier ?? AGGREGATE_COUNT_MINIMUM_TIER;
|
|
77
|
+
if (ctx.treeDepth < minimumTier) {
|
|
78
|
+
// Cold / unpromoted root: no tier-1 children to summarize (matches the seeker's NoState fallback).
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const targetTier = ctx.targetTier ?? DEFAULT_SWEEP_TARGET_TIER;
|
|
83
|
+
const fanout = ctx.fanout ?? DEFAULT_FANOUT;
|
|
84
|
+
const bucketCounts: AggregateBucketV1[] = [];
|
|
85
|
+
for (let prefixSlot = 0; prefixSlot < fanout; prefixSlot++) {
|
|
86
|
+
const count = logBucketCount(ctx.shardCounts.get(prefixSlot) ?? 0);
|
|
87
|
+
if (count > 0 || ctx.includeEmptyShards === true) {
|
|
88
|
+
bucketCounts.push({ targetTier, prefixSlot, count });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const unsigned: Omit<AggregateCountV1, "signature" | "signers"> = {
|
|
93
|
+
v: 1,
|
|
94
|
+
topicId: bytesToB64url(ctx.topicId),
|
|
95
|
+
bucketCounts,
|
|
96
|
+
cohortEpoch: bytesToB64url(ctx.cohortEpoch),
|
|
97
|
+
};
|
|
98
|
+
const { thresholdSig, signers } = await ctx.thresholdSign(aggregateCountSigningPayload(unsigned));
|
|
99
|
+
return {
|
|
100
|
+
...unsigned,
|
|
101
|
+
signature: bytesToB64url(thresholdSig),
|
|
102
|
+
signers: signers.map(bytesToB64url),
|
|
103
|
+
};
|
|
104
|
+
}
|
package/src/matchmaking/index.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Matchmaking — db-p2p wiring to the cohort-topic substrate.
|
|
3
|
-
*
|
|
4
|
-
* The db-core `matchmaking` module owns the wire codecs, anchor, config, provider/seeker
|
|
5
|
-
* decision/state, the capability filter, the pure query evaluation, and the hang-out decision engine;
|
|
6
|
-
* these db-p2p bindings wire that logic to the cohort-topic substrate: the register/renew/withdraw
|
|
7
|
-
* managers (tier T2), the cohort-side `QueryV1` handler, and the seeker walk client that drives the
|
|
8
|
-
* hang-out-vs-continue walk. See `docs/matchmaking.md` §Provider registration / §Seeker query /
|
|
9
|
-
* §Hang-out vs. continue.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export * from "./protocols.js";
|
|
13
|
-
export * from "./provider-manager.js";
|
|
14
|
-
export * from "./seeker-manager.js";
|
|
15
|
-
export * from "./query-handler.js";
|
|
16
|
-
export * from "./query-transport.js";
|
|
17
|
-
export * from "./seeker-walk-client.js";
|
|
18
|
-
export * from "./aggregate-counts.js";
|
|
19
|
-
export * from "./traffic-validation.js";
|
|
20
|
-
export * from "./module.js";
|
|
1
|
+
/**
|
|
2
|
+
* Matchmaking — db-p2p wiring to the cohort-topic substrate.
|
|
3
|
+
*
|
|
4
|
+
* The db-core `matchmaking` module owns the wire codecs, anchor, config, provider/seeker
|
|
5
|
+
* decision/state, the capability filter, the pure query evaluation, and the hang-out decision engine;
|
|
6
|
+
* these db-p2p bindings wire that logic to the cohort-topic substrate: the register/renew/withdraw
|
|
7
|
+
* managers (tier T2), the cohort-side `QueryV1` handler, and the seeker walk client that drives the
|
|
8
|
+
* hang-out-vs-continue walk. See `docs/matchmaking.md` §Provider registration / §Seeker query /
|
|
9
|
+
* §Hang-out vs. continue.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export * from "./protocols.js";
|
|
13
|
+
export * from "./provider-manager.js";
|
|
14
|
+
export * from "./seeker-manager.js";
|
|
15
|
+
export * from "./query-handler.js";
|
|
16
|
+
export * from "./query-transport.js";
|
|
17
|
+
export * from "./seeker-walk-client.js";
|
|
18
|
+
export * from "./aggregate-counts.js";
|
|
19
|
+
export * from "./traffic-validation.js";
|
|
20
|
+
export * from "./module.js";
|