querysub 0.504.0 → 0.506.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/bin/function.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  process.argv.push("--local");
4
- // Only dev. If we listen to all functions it will start picking up production functions calls, which breaks things (unlike the path value server, where we are unreachable, so nothing breaks).
5
- process.argv.push("--filter");
6
- process.argv.push("dev");
4
+ // Only the dev network. If we listen to all networks it will start picking up production function calls, which breaks things (unlike the path value server, where we are unreachable, so nothing breaks).
5
+ process.argv.push("--networkfile");
6
+ process.argv.push("~/devnetwork.txt");
7
7
 
8
8
  require("typenode");
9
9
  require("../src/3-path-functions/PathFunctionRunnerMain");
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ process.argv.push("--local");
4
+ // Serves the dev network AND the default network, so one server can satisfy paths with no network as well as paths on our dev network.
5
+ process.argv.push("--networkfile");
6
+ process.argv.push("~/devnetwork.txt");
7
+ process.argv.push("--network");
8
+ process.argv.push("default");
9
+
10
+ require("typenode");
11
+ require("../src/server.ts");
package/bin/server.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  process.argv.push("--local");
4
+ process.argv.push("--networkfile");
5
+ process.argv.push("~/devnetwork.txt");
4
6
 
5
7
  require("typenode");
6
8
  require("../src/server.ts");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.504.0",
3
+ "version": "0.506.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -31,6 +31,7 @@
31
31
  "deploy": "./bin/deploy.js",
32
32
  "deploy-prefixes": "./bin/deploy-prefixes.js",
33
33
  "server": "./bin/server.js",
34
+ "server-dev": "./bin/server-dev.js",
34
35
  "server-public": "./bin/server-public.js",
35
36
  "function": "./bin/function.js",
36
37
  "function-public": "./bin/function-public.js",
@@ -219,6 +219,7 @@ class AuthorityLookup {
219
219
  prefixes: prefixes.map(p => parsePrefixMatcher(p)),
220
220
  routeStart: 0,
221
221
  routeEnd: 1,
222
+ networks: "all",
222
223
  }, true);
223
224
  return;
224
225
  }
@@ -8,6 +8,7 @@ import { getOwnNodeId, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
8
8
  import { unique } from "../misc";
9
9
  import { measureFnc } from "socket-function/src/profiling/measure";
10
10
  import { getRoutingOverride, hasPrefixHash } from "./PathRouterRouteOverride";
11
+ import { DEFAULT_NETWORK } from "../config";
11
12
  import { sha256 } from "js-sha256";
12
13
  import { rangesOverlap, removeRange } from "../rangeMath";
13
14
  import { decodeParentFilter } from "./hackedPackedPathParentFiltering";
@@ -37,8 +38,23 @@ export type AuthoritySpec = {
37
38
  prefixes: PrefixMatcher[];
38
39
  // - Make sure to set this if you just want the prefix values, otherwise you will get all that that prefixes don't match (the prefixes exclude prefix match but where route does not match).
39
40
  excludeDefault?: boolean;
41
+ // The networks this authority satisfies. Unset is equivalent to ["default"]. Paths only route to non-default networks if their routing override key explicitly asks for that network. "all" is ONLY for client proxy specs (the querysub server the client talks to proxies to the real authorities, which do their own network routing), and is not valid for real authorities.
42
+ networks?: string[] | "all";
40
43
  };
41
44
 
45
+ function getSpecNetworkList(networks: string[] | undefined): string[] {
46
+ return networks && networks.length > 0 && networks || [DEFAULT_NETWORK];
47
+ }
48
+ export function specSatisfiesNetwork(spec: AuthoritySpec, network: string | undefined): boolean {
49
+ if (spec.networks === "all") return true;
50
+ return getSpecNetworkList(spec.networks).includes(network || DEFAULT_NETWORK);
51
+ }
52
+ export function networksOverlap(spec1: AuthoritySpec, spec2: AuthoritySpec): boolean {
53
+ if (spec1.networks === "all" || spec2.networks === "all") return true;
54
+ let networks2 = getSpecNetworkList(spec2.networks);
55
+ return getSpecNetworkList(spec1.networks).some(x => networks2.includes(x));
56
+ }
57
+
42
58
  // If you want to match just a simple path, use parsePrefixMatcher. It won't match ===, so get the parent if you want that (and then filter the result).
43
59
  export type PrefixMatcher = {
44
60
  prefix: string;
@@ -142,7 +158,8 @@ export class PathRouter {
142
158
  let { path, spec } = config;
143
159
  path = hack_stripPackedPath(path);
144
160
  let override = getRoutingOverride(path);
145
- if (override) {
161
+ if (!specSatisfiesNetwork(spec, override?.network)) return -1;
162
+ if (override && override.route !== undefined) {
146
163
  if (spec.excludeDefault && !hasPrefixHash({ spec, prefixHash: override.prefixHash })) return -1;
147
164
  if (override.route < spec.routeStart || override.route >= spec.routeEnd) return -1;
148
165
  return override.route;
@@ -185,7 +202,7 @@ export class PathRouter {
185
202
  public static getSingleKeyRoute(key: string): number {
186
203
  if (key && this.lastKeyRoute.key === key) return this.lastKeyRoute.route;
187
204
  let override = getRoutingOverride(key);
188
- if (override) {
205
+ if (override && override.route !== undefined) {
189
206
  this.lastKeyRoute.key = key;
190
207
  this.lastKeyRoute.route = override.route;
191
208
  return override.route;
@@ -331,6 +348,7 @@ export class PathRouter {
331
348
  prefixes: group.prefixes,
332
349
  routeStart: 0,
333
350
  routeEnd: 1,
351
+ networks: ourSpec.networks,
334
352
  }
335
353
  });
336
354
  let routeIndex = Math.floor(route * splitCount);
@@ -374,6 +392,7 @@ export class PathRouter {
374
392
 
375
393
  @measureFnc
376
394
  public static overlapsAuthority(authority1: AuthoritySpec, authority2: AuthoritySpec): boolean {
395
+ if (!networksOverlap(authority1, authority2)) return false;
377
396
  // TODO: This becomes complicated because of exclude default, although I feel like there has to be a way to simplify it? Eh... whatever.
378
397
 
379
398
  let doRangesOverlap = rangesOverlap({ start: authority1.routeStart, end: authority1.routeEnd }, { start: authority2.routeStart, end: authority2.routeEnd });
@@ -438,6 +457,7 @@ export class PathRouter {
438
457
  let { target } = config;
439
458
  let allSources = authorityLookup.getTopologySync();
440
459
  allSources = allSources.filter(x => !isOwnNodeId(x.nodeId));
460
+ allSources = allSources.filter(x => networksOverlap(x.authoritySpec, target));
441
461
  // THIS is normal during initial server startup
442
462
  if (!allSources.length) {
443
463
  return [];
@@ -557,10 +577,12 @@ export class PathRouter {
557
577
  range: { start: number; end: number };
558
578
  }[];
559
579
  } {
560
- let parentRange = decodeParentFilter(path) || {
580
+ let parentFilter = decodeParentFilter(path);
581
+ let parentRange = parentFilter || {
561
582
  start: 0,
562
583
  end: 1,
563
584
  };
585
+ let parentNetwork = parentFilter?.network;
564
586
  path = hack_stripPackedPath(path);
565
587
  let preferredNodeIds = new Set(config?.preferredNodeIds ?? []);
566
588
 
@@ -569,6 +591,7 @@ export class PathRouter {
569
591
  // - The different route case is how the FuntionRunner works, and without it large databases couldn't run functions. However, most applications won't directly use it.
570
592
  // NOTE: The only own nodes flag is actually so we can access this before the topology finishes synchronizing. Because we want to be able to call get child read nodes from path value core for some really basic stuff. It also is what the caller wants, but that's not a good enough reason to add this check. The reason we have this check is because without it, the topology won't have finished synchronizing and startup won't work.
571
593
  let allSources = config?.onlyOwnNodes ? [{ nodeId: getOwnNodeId(), authoritySpec: authorityLookup.getOurSpec() }] : authorityLookup.getTopologySync();
594
+ allSources = allSources.filter(x => specSatisfiesNetwork(x.authoritySpec, parentNetwork));
572
595
  // Prefer our own node
573
596
  sort(allSources, x => isOwnNodeId(x.nodeId) ? -1 : 1);
574
597
 
@@ -674,7 +697,7 @@ export class PathRouter {
674
697
  if (!config?.onlyOwnNodes) {
675
698
 
676
699
  // NOTE: We *could* actually synchronize it even if it doesn't have a prefix shard as we can fall back to just the full path sharding. However, it becomes very complicated if we want a specific range, and then it becomes complicated if it then switches to prefix hashing (With the nodes that were using the full path hashing slowly going away). AND... key synchronization IS slow, so it's good to discourage it in general.
677
- console.error(`Want to sync a prefix which is not under an existing prefix, nor equal to a prefix. 1) The servers are down. 1.5) The servers are in an inconsistent state (one shard range added a parent path, the other didn't). 2) Don't access the .keys() 3) call addRoutingPrefixForDeploy to add a route/parent route explicitly (as is done in PathFunctionRunner.ts). Path: ${JSON.stringify(path)}`, { path, allSources: allSources.map(x => x.authoritySpec) });
700
+ console.error(`Want to sync a prefix which is not under an existing prefix, nor equal to a prefix. 1) The servers are down. 1.5) The servers are in an inconsistent state (one shard range added a parent path, the other didn't). 2) Don't access the .keys() 3) call addRoutingPrefixForDeploy to add a route/parent route explicitly (as is done in PathFunctionRunner.ts). 4) No server is on the network (only servers on the network can satisfy it). Path: ${JSON.stringify(path)}, network: ${parentNetwork || DEFAULT_NETWORK}`, { path, network: parentNetwork || DEFAULT_NETWORK, allSources: allSources.map(x => x.authoritySpec) });
678
701
  }
679
702
  return { nodes: [] };
680
703
  }
@@ -2,13 +2,12 @@ import { cacheLimited } from "socket-function/src/caching";
2
2
  import { AuthoritySpec, PathRouter } from "./PathRouter";
3
3
  import { sha256 } from "js-sha256";
4
4
  import { getPathFromStr } from "../path";
5
+ import { DEFAULT_NETWORK } from "../config";
5
6
 
6
7
  function getPrefixHash(prefix: string): string {
7
8
  return Buffer.from(sha256(prefix), "hex").toString("base64").slice(0, 12);
8
9
  }
9
10
 
10
- // So... should be use unicode characters?
11
- // - Both to ensure all of our code supports them in keys, and because it allows us to us a lot fewer characters.
12
11
  // NOTE: If we want, we could add code to prevent users from creating keys that use these. However, I think it's fine, because the querysub server is still the first node you talk to, so it can throttle your traffic. ALSO, ideally all of your traffic DOES go to the same PathValueServer. That's why prefix routing exists. And PathValueServers should be able to handle A LOT of traffic.
13
12
  let keySpecialIdentifier = (
14
13
  "ROUTE_"
@@ -18,22 +17,102 @@ let keySpecialIdentifier = (
18
17
  + String.fromCharCode(0xF4D7)
19
18
  );
20
19
 
21
- // NOTE: ONLY works for direct accesses, not for child key accesses. Also, it only works if the prefix is matched. If nothing matches the prefix, then this will actually make it not match any authorities.
20
+ export function assertValidNetwork(network: string) {
21
+ if (!/^[a-z]+$/.test(network)) {
22
+ throw new Error(`Networks must be lowercase letters (a-z) only, was ${JSON.stringify(network)}`);
23
+ }
24
+ }
25
+
26
+ // The route slot is either a route number ("0.12345"), a network ("dev"), or both ("dev:0.12345"). Networks are strictly lowercase letters and routes are strictly numeric, so the forms are unambiguous.
27
+ function encodeRouteSlot(config: { route?: number; network?: string }): string {
28
+ let { route, network } = config;
29
+ let routeStr = route !== undefined && route.toString().slice(0, 7) || "";
30
+ if (network && routeStr) return `${network}:${routeStr}`;
31
+ if (network) return network;
32
+ return routeStr;
33
+ }
34
+ function decodeRouteSlot(slot: string): { route?: number; network?: string } | undefined {
35
+ if (!slot) return undefined;
36
+ let network: string | undefined;
37
+ let routeStr = slot;
38
+ let colonIndex = slot.indexOf(":");
39
+ if (colonIndex >= 0) {
40
+ network = slot.slice(0, colonIndex);
41
+ routeStr = slot.slice(colonIndex + 1);
42
+ } else if (/^[a-z]+$/.test(slot)) {
43
+ return { network: slot };
44
+ }
45
+ if (network && !/^[a-z]+$/.test(network)) return undefined;
46
+ let route = parseFloat(routeStr);
47
+ if (isNaN(route)) return undefined;
48
+ return { route, network };
49
+ }
50
+
51
+ export function createRoutingOverrideKeyBase(config: {
52
+ originalKey: string;
53
+ // Hashed to get the route, if route is not directly provided
54
+ routeKey?: string;
55
+ route?: number;
56
+ network?: string;
57
+ // This is the prefix it has the equivalent of. We need this, so if something excludes default, it doesn't automatically get every routing overridden value. Required whenever a route (or routeKey) is set.
58
+ remappedPrefix?: string;
59
+ }) {
60
+ let { originalKey, routeKey, route, network, remappedPrefix } = config;
61
+ if (network) {
62
+ assertValidNetwork(network);
63
+ }
64
+ if (route === undefined && routeKey !== undefined) {
65
+ route = PathRouter.getSingleKeyRoute(routeKey);
66
+ }
67
+ if (route === undefined && !network) {
68
+ throw new Error(`createRoutingOverrideKeyBase requires at least one of route, routeKey, or network`);
69
+ }
70
+ if (route !== undefined && !remappedPrefix) {
71
+ throw new Error(`createRoutingOverrideKeyBase requires remappedPrefix when a route is set, so excludeDefault authorities can match it correctly`);
72
+ }
73
+ let prefixHash = remappedPrefix && getPrefixHash(remappedPrefix) || "";
74
+ return keySpecialIdentifier + "!" + prefixHash + "!" + encodeRouteSlot({ route, network }) + "!" + originalKey;
75
+ }
76
+
22
77
  export function createRoutingOverrideKey(config: {
23
78
  originalKey: string;
24
79
  routeKey: string;
25
- // This is the prefix it has the equivalent of. We need this, so if something excludes default, it doesn't automatically get every routing overridden value.
26
80
  remappedPrefix: string;
81
+ network?: string;
82
+ }) {
83
+ return createRoutingOverrideKeyBase(config);
84
+ }
85
+
86
+ /** Creates a key that routes to a specific network, but otherwise routes normally (via prefix / full path hashing). */
87
+ export function createNetworkKey(config: {
88
+ originalKey: string;
89
+ network: string;
27
90
  }) {
28
- let { originalKey, routeKey, remappedPrefix } = config;
29
- let route = PathRouter.getSingleKeyRoute(routeKey);
30
- return keySpecialIdentifier + "!" + getPrefixHash(remappedPrefix) + "!" + route.toString().slice(0, 7) + "!" + originalKey;
91
+ return createRoutingOverrideKeyBase({ originalKey: config.originalKey, network: config.network });
31
92
  }
32
93
 
33
- export function getRoutingOverride(path: string): {
34
- route: number;
94
+ /** Rewrites the routing override part inside key (which must contain one) to be on the given network. */
95
+ export function setRoutingOverrideKeyNetwork(key: string, network: string): string {
96
+ assertValidNetwork(network);
97
+ if (!key.startsWith(keySpecialIdentifier)) {
98
+ return createNetworkKey({ originalKey: key, network });
99
+ }
100
+ let parts = key.split("!");
101
+ if (parts.length < 4) {
102
+ return createNetworkKey({ originalKey: key, network });
103
+ }
104
+ let slot = decodeRouteSlot(parts[2]) || {};
105
+ parts[2] = encodeRouteSlot({ route: slot.route, network });
106
+ return parts.join("!");
107
+ }
108
+
109
+ export type RoutingOverride = {
35
110
  prefixHash: string;
36
- } | undefined {
111
+ route?: number;
112
+ network?: string;
113
+ };
114
+
115
+ export function getRoutingOverride(path: string): RoutingOverride | undefined {
37
116
  if (!path.includes(keySpecialIdentifier)) return undefined;
38
117
  let parts = getPathFromStr(path);
39
118
  for (let part of parts) {
@@ -42,22 +121,24 @@ export function getRoutingOverride(path: string): {
42
121
  }
43
122
  return undefined;
44
123
  }
45
- export function getRoutingOverridePart(part: string): {
46
- prefixHash: string;
47
- route: number;
48
- } | undefined {
124
+ export function getRoutingOverridePart(part: string): RoutingOverride | undefined {
49
125
  if (!part.startsWith(keySpecialIdentifier)) return undefined;
50
126
  let parts = part.split("!");
51
127
  if (parts.length < 4) return undefined;
52
128
  let prefixHash = parts[1];
53
- let route = parseFloat(parts[2]);
54
- if (isNaN(route)) return undefined;
129
+ let slot = decodeRouteSlot(parts[2]);
130
+ if (!slot) return undefined;
55
131
  return {
56
132
  prefixHash,
57
- route,
133
+ route: slot.route,
134
+ network: slot.network,
58
135
  };
59
136
  }
60
137
 
138
+ export function getPathNetwork(path: string): string {
139
+ return getRoutingOverride(path)?.network || DEFAULT_NETWORK;
140
+ }
141
+
61
142
  export const hasPrefixHash = cacheLimited(1000 * 10,
62
143
  (config: { spec: AuthoritySpec, prefixHash: string }) => {
63
144
  let { spec, prefixHash } = config;
@@ -69,4 +150,3 @@ export const hasPrefixHash = cacheLimited(1000 * 10,
69
150
  return false;
70
151
  }
71
152
  );
72
-
@@ -1,7 +1,7 @@
1
1
  import path from "path";
2
2
  import { AuthoritySpec, parsePrefixMatcher } from "./PathRouter";
3
3
  import { getPathDepth } from "../path";
4
- import { getAuthorityRange, getAuthorityExcludeDefault, getAuthorityPrefix } from "../config";
4
+ import { getAuthorityRange, getAuthorityExcludeDefault, getAuthorityPrefix, getNetworks } from "../config";
5
5
  import { getShardPrefixes } from "./ShardPrefixes";
6
6
  import { getOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
7
7
 
@@ -22,6 +22,7 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
22
22
  routeStart: 0,
23
23
  routeEnd: 1,
24
24
  prefixes: prefixMatchers,
25
+ networks: getNetworks(),
25
26
  };
26
27
  }
27
28
  return undefined;
@@ -46,6 +47,7 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
46
47
  routeEnd: rangeEnd,
47
48
  prefixes: usePrefixes,
48
49
  excludeDefault: excludeDefault || undefined,
50
+ networks: getNetworks(),
49
51
  };
50
52
  }
51
53
 
@@ -67,5 +69,6 @@ export async function getAllAuthoritySpec(): Promise<AuthoritySpec> {
67
69
  routeStart: 0,
68
70
  routeEnd: 1,
69
71
  prefixes: prefixMatchers,
72
+ networks: getNetworks(),
70
73
  };
71
74
  }
@@ -20,6 +20,8 @@ import { debugNodeId } from "../-c-identity/IdentityController";
20
20
  import { decodeNodeId } from "sliftutils/misc/https/certs";
21
21
  import { getDomain } from "../config";
22
22
  import { decodeParentFilter, encodeParentFilter } from "./hackedPackedPathParentFiltering";
23
+ import { getPathNetwork } from "./PathRouterRouteOverride";
24
+ import { DEFAULT_NETWORK } from "../config";
23
25
  import { deepCloneCborx } from "../misc/cloneHelpers";
24
26
  import { removeRange } from "../rangeMath";
25
27
  import { registerShutdownHandler } from "../diagnostics/periodic";
@@ -205,8 +207,9 @@ class PathValueCommitter {
205
207
  parentSyncs: [],
206
208
  initialTriggers: { values: new Set(), parentPaths: new Set() },
207
209
  });
208
- console.error(`There are no authorities for path ${pathValue.path}. The write will be lost.`, {
210
+ console.error(`There are no authorities for path ${pathValue.path} (network ${getPathNetwork(pathValue.path)}). The write will be lost.`, {
209
211
  path: pathValue.path,
212
+ network: getPathNetwork(pathValue.path),
210
213
  timeId: pathValue.time.time,
211
214
  source: pathValue.source,
212
215
  otherAuthorities,
@@ -349,6 +352,7 @@ class PathValueCommitter {
349
352
  remoteWatcher.getExistingWatchRemoteNodeId(path);
350
353
  console.warn(`Ignoring value from wrong authority. Should have been ${debugNodeId(watchingAuthorityId)}, but was received from ${debugNodeId(batch.sourceNodeId)}.`, {
351
354
  path,
355
+ network: getPathNetwork(path),
352
356
  type,
353
357
  timeId: value?.time.time,
354
358
  source: value?.source,
@@ -390,6 +394,7 @@ class PathValueCommitter {
390
394
 
391
395
  console.warn(`Ignoring parent path which we aren't watching. From ${debugNodeId(batch.sourceNodeId)}.`, {
392
396
  parentPath,
397
+ network: decodeParentFilter(parentPath)?.network || DEFAULT_NETWORK,
393
398
  sourceNodeId: debugNodeId(batch.sourceNodeId),
394
399
  sourceNodeThreadId: decodeNodeId(batch.sourceNodeId, getDomain())?.threadId,
395
400
  });
@@ -9,7 +9,9 @@ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSeriali
9
9
  import { pathValueCommitter } from "./PathValueCommitter";
10
10
  import { auditLog, isDebugLogEnabled } from "./auditLogs";
11
11
  import { debugNodeId, debugNodeThread } from "../-c-identity/IdentityController";
12
- import { getSlowdown, isDiskAudit, getDomain } from "../config";
12
+ import { getSlowdown, isDiskAudit, getDomain, DEFAULT_NETWORK } from "../config";
13
+ import { getPathNetwork } from "./PathRouterRouteOverride";
14
+ import { decodeParentFilter } from "./hackedPackedPathParentFiltering";
13
15
  import { decodeNodeId } from "sliftutils/misc/https/certs";
14
16
  import { areNodeIdsEqual, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
15
17
  import { getNodeIdIP } from "socket-function/src/nodeCache";
@@ -233,10 +235,10 @@ export class PathValueControllerBase {
233
235
  if (isDebugLogEnabled()) {
234
236
  let sourceNodeId = debugNodeId(callerId);
235
237
  for (let value of config.paths) {
236
- auditLog("WATCH PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
238
+ auditLog("WATCH PATH", { path: value, network: getPathNetwork(value), sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
237
239
  }
238
240
  for (let value of config.parentPaths) {
239
- auditLog("WATCH PARENT PATH", { path: value, sourceNodeId });
241
+ auditLog("WATCH PARENT PATH", { path: value, network: decodeParentFilter(value)?.network || DEFAULT_NETWORK, sourceNodeId });
240
242
  }
241
243
  }
242
244
  pathWatcher.watchPath({
@@ -252,10 +254,10 @@ export class PathValueControllerBase {
252
254
  if (isDebugLogEnabled()) {
253
255
  let sourceNodeId = debugNodeId(SocketFunction.getCaller().nodeId);
254
256
  for (let value of config.paths) {
255
- auditLog("UNWATCHING PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
257
+ auditLog("UNWATCHING PATH", { path: value, network: getPathNetwork(value), sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
256
258
  }
257
259
  for (let value of config.parentPaths) {
258
- auditLog("UNWATCHING PARENT PATH", { path: value, sourceNodeId });
260
+ auditLog("UNWATCHING PARENT PATH", { path: value, network: decodeParentFilter(value)?.network || DEFAULT_NETWORK, sourceNodeId });
259
261
  }
260
262
  }
261
263
  pathWatcher.unwatchPath({ paths: config.paths, parentPaths: config.parentPaths, callback: callerId, reason: "PathValueController.unwatchLatest" });
@@ -11,7 +11,9 @@ import { PathValueControllerBase } from "./PathValueController";
11
11
  import { PathRouter } from "./PathRouter";
12
12
  import { auditLog } from "./auditLogs";
13
13
  import { WatchConfig, authorityStorage, PathValue, NodeId, compareTime, isCoreQuiet, MAX_CHANGE_AGE, createMissingEpochValue } from "./pathValueCore";
14
- import { matchesParentRangeFilter } from "./hackedPackedPathParentFiltering";
14
+ import { decodeParentFilter, matchesParentRangeFilter } from "./hackedPackedPathParentFiltering";
15
+ import { getPathNetwork } from "./PathRouterRouteOverride";
16
+ import { DEFAULT_NETWORK } from "../config";
15
17
  import { isClient } from "../config2";
16
18
  import { delay } from "socket-function/src/batching";
17
19
  import { isClientNodeId } from "socket-function/src/nodeCache";
@@ -170,21 +172,22 @@ class PathWatcher {
170
172
  incrementWatcherSequence();
171
173
  if (isOwnNodeId(config.nodeId)) {
172
174
  for (let path of newPathsWatched) {
173
- auditLog("new local WATCH VALUE", { path });
175
+ auditLog("new local WATCH VALUE", { path, network: getPathNetwork(path) });
174
176
  }
175
177
  for (let path of newParentsWatched) {
176
- auditLog("new local WATCH PARENT", { path });
178
+ auditLog("new local WATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK });
177
179
  }
178
180
  } else {
179
181
  for (let path of newPathsWatched) {
180
182
  auditLog("new non-local WATCH VALUE", {
181
183
  path,
184
+ network: getPathNetwork(path),
182
185
  watcher: config.nodeId,
183
186
  sourceNodeThreadId: debugNodeThread(config.nodeId),
184
187
  });
185
188
  }
186
189
  for (let path of newParentsWatched) {
187
- auditLog("new non-local WATCH PARENT", { path, watcher: config.nodeId });
190
+ auditLog("new non-local WATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, watcher: config.nodeId });
188
191
  }
189
192
  }
190
193
  console.info(`New PathValue watches`, {
@@ -244,9 +247,9 @@ class PathWatcher {
244
247
  obj.watchers.delete(callback);
245
248
 
246
249
  if (isOwnNodeId(callback)) {
247
- auditLog("local UNWATCH PARENT", { path, reason });
250
+ auditLog("local UNWATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, reason });
248
251
  } else {
249
- auditLog("non-local UNWATCH PARENT", { path, watcher: callback, remoteNodeId: debugNodeId(callback), remoteNodeThreadId: debugNodeThread(callback), reason });
252
+ auditLog("non-local UNWATCH PARENT", { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK, watcher: callback, remoteNodeId: debugNodeId(callback), remoteNodeThreadId: debugNodeThread(callback), reason });
250
253
  }
251
254
 
252
255
  if (obj.watchers.size === 0) {
@@ -281,9 +284,9 @@ class PathWatcher {
281
284
  watchers.watchers.delete(callback);
282
285
 
283
286
  if (isOwnNodeId(callback)) {
284
- auditLog("local UNWATCH VALUE", { path, reason });
287
+ auditLog("local UNWATCH VALUE", { path, network: getPathNetwork(path), reason });
285
288
  } else {
286
- auditLog("non-local UNWATCH VALUE", { path, watcher: callback, reason });
289
+ auditLog("non-local UNWATCH VALUE", { path, network: getPathNetwork(path), watcher: callback, reason });
287
290
  }
288
291
 
289
292
  if (watchers.watchers.size === 0) {
@@ -3,6 +3,8 @@ import { getPathDepth, getPathIndexAssert, hack_getPackedPathSuffix, hack_setPac
3
3
  import { AuthoritySpec, PathRouter } from "./PathRouter";
4
4
  import { cache } from "socket-function/src/caching";
5
5
  import { authorityLookup } from "./AuthorityLookup";
6
+ import { assertValidNetwork, getPathNetwork } from "./PathRouterRouteOverride";
7
+ import { DEFAULT_NETWORK } from "../config";
6
8
 
7
9
 
8
10
  let getSpecForChildPath = (path: string) => authorityLookup.getOurSpec();
@@ -15,10 +17,15 @@ export function matchesParentRangeFilter(config: {
15
17
  fullPath: string;
16
18
  packedPath: string;
17
19
  }) {
18
- // If it equals the non packed path, then it must not be packed, and so it must match.
19
- if (config.parentPath === config.packedPath) return true;
20
+ // If it equals the non packed path, then it must not be packed, and so it must match.
21
+ if (config.parentPath === config.packedPath) {
22
+ return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
23
+ }
20
24
  let filter = decodeParentFilter(config.packedPath);
21
- if (!filter) return true;
25
+ if (!filter) {
26
+ return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
27
+ }
28
+ if (getPathNetwork(config.fullPath) !== (filter.network || DEFAULT_NETWORK)) return false;
22
29
  if (filter.start <= 0 && filter.end >= 1) return true;
23
30
  let route = PathRouter.getRouteFull({ path: config.fullPath, spec: getSpecForChildPath(config.fullPath) });
24
31
  return filter.start <= route && route < filter.end;
@@ -26,12 +33,14 @@ export function matchesParentRangeFilter(config: {
26
33
 
27
34
  export const filterChildPathsBase = measureWrap(
28
35
  function filterChildPathsBase(parentPath: string, packedSuffix: string, paths: Set<string>): Set<string> {
29
- let [startFractionStr, endFractionStr] = packedSuffix.split("|");
36
+ let [startFractionStr, endFractionStr, network] = packedSuffix.split("|");
30
37
  let startFraction = Number(startFractionStr);
31
38
  let endFraction = Number(endFractionStr);
39
+ let filterNetwork = network || DEFAULT_NETWORK;
32
40
 
33
41
  let filtered = new Set<string>();
34
42
  for (let path of paths) {
43
+ if (getPathNetwork(path) !== filterNetwork) continue;
35
44
  // TODO: We can make this significantly more efficient as once we know if it's a prefix or not, we know the underlying function call every time. However, that starts to get complicated, and I don't think this is going to be a bottleneck.
36
45
  let route = PathRouter.getRouteFull({ path, spec: getSpecForChildPath(path) });
37
46
  if (startFraction <= route && route < endFraction) {
@@ -47,14 +56,19 @@ export function encodeParentFilter(config: {
47
56
  path: string;
48
57
  startFraction: number;
49
58
  endFraction: number;
59
+ network?: string;
50
60
  }) {
51
- return hack_setPackedPathSuffix(config.path, `${config.startFraction}|${config.endFraction}`);
61
+ let suffix = `${config.startFraction}|${config.endFraction}`;
62
+ if (config.network) {
63
+ assertValidNetwork(config.network);
64
+ suffix += `|${config.network}`;
65
+ }
66
+ return hack_setPackedPathSuffix(config.path, suffix);
52
67
  }
53
- export function decodeParentFilter(path: string): { path: string; start: number, end: number } | undefined {
68
+ export function decodeParentFilter(path: string): { path: string; start: number, end: number; network?: string } | undefined {
54
69
  let packedSuffix = hack_getPackedPathSuffix(path);
55
70
  if (!packedSuffix) return undefined;
56
- let [startStr, endStr] = packedSuffix.split("|");
71
+ let [startStr, endStr, network] = packedSuffix.split("|");
57
72
  let unpackedPath = hack_stripPackedPath(path);
58
- return { path: unpackedPath, start: Number(startStr), end: Number(endStr) };
59
-
60
- }
73
+ return { path: unpackedPath, start: Number(startStr), end: Number(endStr), network: network || undefined };
74
+ }
@@ -24,7 +24,8 @@ import { fastHash } from "../misc/hash";
24
24
  import { authorityLookup } from "./AuthorityLookup";
25
25
  import { onPathInteracted } from "../diagnostics/pathAuditerCallback";
26
26
  import { decodeParentFilter, filterChildPathsBase } from "./hackedPackedPathParentFiltering";
27
- import { isDiskAudit } from "../config";
27
+ import { getPathNetwork } from "./PathRouterRouteOverride";
28
+ import { isDiskAudit, DEFAULT_NETWORK } from "../config";
28
29
  import { removeRange } from "../rangeMath";
29
30
  import { remoteWatcher } from "../1-path-client/RemoteWatcher";
30
31
  import { setFlag } from "socket-function/require/compileFlags";
@@ -569,18 +570,20 @@ class AuthorityPathValueStorage {
569
570
  if (this.DEBUG_UNWATCH) {
570
571
  console.log(blue(`Unsyncing path at ${Date.now()}`), path);
571
572
  }
572
- auditLog("DESTROY PATH", { path });
573
+ auditLog("DESTROY PATH", { path, network: getPathNetwork(path) });
573
574
 
574
575
  this.isSyncedCache.delete(path);
575
576
  this.removePathFromStorage(path, "unwatched");
576
577
  }
577
578
  public markParentPathAsUnwatched(path: string) {
578
579
  if (this.parentsSynced.has(path)) {
579
- console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path });
580
+ console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path, network: decodeParentFilter(path)?.network || DEFAULT_NETWORK });
580
581
  }
581
582
  // NOTE: I don't think we have to handle the case where we are the authority,
582
583
  // because we don't delete any actual data here.
583
584
  this.parentsSynced.delete(path);
585
+ let decoded = decodeParentFilter(path);
586
+ this.parentsSynced.delete(this.parentSyncKey(hack_stripPackedPath(path), decoded?.network));
584
587
  }
585
588
 
586
589
  /** Used for atomic operations, to ensure a path is stable enough (and to check that it hasn't
@@ -607,6 +610,11 @@ class AuthorityPathValueStorage {
607
610
  }
608
611
  return true;
609
612
  }
613
+ private parentSyncKey(strippedParentPath: string, network: string | undefined): string {
614
+ if (!network || network === DEFAULT_NETWORK) return strippedParentPath;
615
+ return strippedParentPath + String.fromCharCode(1) + "network=" + network;
616
+ }
617
+
610
618
  public isSynced(path: string) {
611
619
  if (PathRouter.isSelfAuthority(path)) return true;
612
620
 
@@ -635,7 +643,7 @@ class AuthorityPathValueStorage {
635
643
  let remoteWatchRoute = remoteWatcher.getRemoteParentWatchRoute(path);
636
644
  if (remoteWatchRoute !== undefined) {
637
645
  let parent = getParentPathStr(path);
638
- let ranges = this.parentsSynced.get(parent);
646
+ let ranges = this.parentsSynced.get(this.parentSyncKey(parent, getPathNetwork(path)));
639
647
  // NOTE: We can't cache this because when we stop watching the parent, the remote watch will remove it, which will cause us to be no longer synced. However, it's not going to know to go into our synced cache and remove it.
640
648
  if (ranges === true) return true;
641
649
  if (ranges) {
@@ -657,7 +665,7 @@ class AuthorityPathValueStorage {
657
665
  parentPath = hack_stripPackedPath(parentPath);
658
666
  if (PathRouter.isLocalPath(parentPath)) return true;
659
667
 
660
- let synced = this.parentsSynced.get(originalPath) || this.parentsSynced.get(parentPath);
668
+ let synced = this.parentsSynced.get(originalPath) || this.parentsSynced.get(this.parentSyncKey(parentPath, range?.network));
661
669
  if (synced === true) return true;
662
670
  // See if the ranges received so far cover the requested range. If we only request a partial range, then this will always be the case. We'll never fully synchronize the path.
663
671
  if (synced && range) {
@@ -694,12 +702,12 @@ class AuthorityPathValueStorage {
694
702
  public addParentSyncs(parentSyncs: { parentPath: string; sourceNodeId: string }[]) {
695
703
  for (let obj of parentSyncs) {
696
704
  if (isDebugLogEnabled()) {
697
- auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath, remoteNodeId: debugNodeId(obj.sourceNodeId), remoteNodeThreadId: debugNodeThread(obj.sourceNodeId) });
705
+ auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath, network: decodeParentFilter(obj.parentPath)?.network || DEFAULT_NETWORK, remoteNodeId: debugNodeId(obj.sourceNodeId), remoteNodeThreadId: debugNodeThread(obj.sourceNodeId) });
698
706
  }
699
707
 
700
708
  let decoded = decodeParentFilter(obj.parentPath);
701
709
  let range = decoded ? { start: decoded.start, end: decoded.end } : { start: 0, end: 1 };
702
- let parentPath = hack_stripPackedPath(obj.parentPath);
710
+ let parentPath = this.parentSyncKey(hack_stripPackedPath(obj.parentPath), decoded?.network);
703
711
 
704
712
  let prevSynced = this.parentsSynced.get(parentPath);
705
713
  if (prevSynced === true) continue;