@yz-social/kdht 0.1.5 → 0.1.7

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/dht/kbucket.js CHANGED
@@ -36,7 +36,7 @@ export class KBucket {
36
36
  }
37
37
  async refresh() { // Refresh specified bucket using LocateNodes for a random key in the specified bucket's range.
38
38
  if (this.node.isStopped() || !this.contacts.length) return false; // fixme skip isStopped?
39
- this.node.log('refresh bucket', this.index);
39
+ this.node.ilog('refresh bucket', this.index);
40
40
  const targetKey = this.randomTarget;
41
41
  await this.node.locateNodes(targetKey); // Side-effect is to update this bucket.
42
42
  return true;
package/dht/node.js CHANGED
@@ -106,7 +106,7 @@ export class Node extends NodeProbe {
106
106
  return k - remaining;
107
107
  }
108
108
  async join(contact) {
109
- this.log('joining', contact.sname);
109
+ this.ilog('joining', contact.sname);
110
110
  contact = this.ensureContact(contact);
111
111
  await contact.connect();
112
112
  await this.addToRoutingTable(contact);
@@ -126,7 +126,7 @@ export class Node extends NodeProbe {
126
126
  // if (!started) started = true;
127
127
  // else if (!bucket?.contacts.length) await this.ensureBucket(index).refresh();
128
128
  // }
129
- this.log('joined', contact.sname);
129
+ this.ilog('joined', contact.sname);
130
130
  return this.contact; // Answering this node's home contact is handy for chaining or keeping track of contacts being made and joined.
131
131
  }
132
132
  }
package/dht/nodeProbe.js CHANGED
@@ -86,7 +86,7 @@ export class NodeProbe extends NodeMessages {
86
86
  let iterationFinished = false;
87
87
 
88
88
  let resolveIteration;
89
- const iterationPromise = new Promise((resolve) => resolveIteration = (...args) => { iterationFinished = true; resolve(...args) }); // to be resolved with a value result, or undefined
89
+ const iterationPromise = new Promise((resolve) => resolveIteration = (...args) => { iterationFinished = true; resolve(...args); }); // to be resolved with a value result, or undefined
90
90
 
91
91
  // Check if termination condition is met:
92
92
  // We're complete when we've found k 'responded' nodes with no unresolved nodes
@@ -166,7 +166,7 @@ export class NodeProbe extends NodeMessages {
166
166
 
167
167
  // Result is array of Helpers (may be empty if node had no new contacts)
168
168
  // Merge new helpers into allNodesSeen and track progress
169
- if (result.length > 0) {
169
+ if (result?.length > 0) {
170
170
  allNodesSeen.push(...result);
171
171
  allNodesSeen.sort(Helper.compare); // Keep sorted by distance (best-first).
172
172
  responsesWithoutNewNodes = 0; // reset counter
@@ -12,7 +12,10 @@ export class NodeStorage extends NodeRefresh {
12
12
  }
13
13
  // TODO: The paper says this can be optimized.
14
14
  // Claude.ai suggests just writing to the next in line, but that doesn't work.
15
- this.schedule(key, 'storage', () => this.storeValue(key, value));
15
+ this.schedule(key, 'storage', () => {
16
+ this.ilog('refresh value', value, 'at key', key);
17
+ this.storeValue(key, value);
18
+ });
16
19
  }
17
20
  retrieveLocally(key) { // Retrieve from memory.
18
21
  return this.storage.get(key);
@@ -12,11 +12,13 @@ export class NodeUtilities {
12
12
  }
13
13
 
14
14
  debug = false;
15
+ info = true;
15
16
  get sname() { // The home contact sname, or just name if no contact
16
17
  return this.contact?.sname || this.name;
17
18
  }
18
- log(...rest) { if (this.debug) console.log(new Date(), this.sname, ...rest); }
19
- xlog(...rest) { console.log(new Date(), this.sname, ...rest); }
19
+ log(...rest) { if (this.debug) this.xlog(new Date(), this.sname, ...rest); }
20
+ ilog(...rest) { if (this.info || this.debug) this.xlog(...rest); }
21
+ xlog(...rest) { console.log(new Date().toISOString(), this.sname, ...rest); }
20
22
  static assert(ok, ...rest) { // If !ok, log rests and exit.
21
23
  if (ok) return;
22
24
  console.error(...rest, new Error("Assert failure").stack); // Not throwing error, because we want to exit. But we are grabbing stack.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yz-social/kdht",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Pure Kademlia base, for testing variations.",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "type": "module",
11
11
  "scripts": {
12
- "start": "node spec/portal.js --externalBaseURL https://yz.social/kdht",
12
+ "start": "node spec/portal.js --externalBaseURL https://ki1r0y.com/kdht",
13
13
  "stop": "pkill kdht",
14
14
  "background": "npm stop; (npm start 1>server.log 2>&1 &); sleep 1",
15
15
  "bots": "node spec/bots.js",
package/portals/node.js CHANGED
@@ -28,7 +28,6 @@ export async function setup({baseURL, externalBaseURL = '', verbose, fixedSpacin
28
28
  const bootstrap = joinURL && await contact.ensureRemoteContact(bootstrapName, joinURL);
29
29
  process.send(contact.sname); // Report in to server as available for others to bootstrap through.
30
30
  if (bootstrap) await contact.join(bootstrap);
31
- contact.host.xlog('joined');
32
31
  process.on('SIGINT', async () => {
33
32
  console.log(process.title, 'Shutdown for Ctrl+C');
34
33
  await contact.disconnect();
package/spec/bots.js CHANGED
@@ -45,8 +45,10 @@ const argv = yargs(hideBin(process.argv))
45
45
  .parse();
46
46
 
47
47
  const host = uuidv4();
48
+ process.title = 'kdht-bot-' + host;
48
49
 
49
50
  if (cluster.isPrimary) {
51
+ console.log(`${cpus()[0].model}, ${logicalCores} logical cores. Starting ${argv.nBots} over ${Node.refreshTimeIntervalMS/1000} seconds.`);
50
52
  for (let i = 1; i < argv.nBots; i++) { // The cluster primary becomes bot 0.
51
53
  cluster.fork();
52
54
  }
@@ -56,7 +58,6 @@ if (cluster.isPrimary) {
56
58
  launchWriteRead(argv.nWrites, argv.baseURL, Node.refreshTimeIntervalMS, argv.verbose);
57
59
  }
58
60
  }
59
- process.title = 'kdht-bot-' + host;
60
61
 
61
62
  await Node.delay(Node.randomInteger(Node.refreshTimeIntervalMS));
62
63
  console.log(cluster.worker?.id || 0, host);
@@ -64,19 +65,17 @@ let contact = await WebContact.create({name: host, debug: argv.verbose});
64
65
  let bootstrapName = await contact.fetchBootstrap(argv.baseURL);
65
66
  let bootstrapContact = await contact.ensureRemoteContact(bootstrapName, argv.baseURL);
66
67
  await contact.join(bootstrapContact);
67
- contact.host.xlog('joined');
68
68
 
69
- // process.on('SIGINT', async () => {
70
- // console.log(process.title, 'Shutdown for Ctrl+C');
71
- // await contact.disconnect();
72
- // process.exit(0);
73
- // });
69
+ process.on('SIGINT', async () => {
70
+ console.log(process.title, 'Shutdown for Ctrl+C');
71
+ await contact.disconnect();
72
+ process.exit(0);
73
+ });
74
74
 
75
75
  while (argv.thrash) {
76
76
  await Node.delay(contact.host.fuzzyInterval(Node.refreshTimeIntervalMS));
77
77
  const old = contact;
78
78
  const next = uuidv4();
79
- contact.host.xlog('disconnecting');
80
79
  await contact.disconnect();
81
80
  await Node.delay(1e3); // TODO: remove?
82
81
 
@@ -84,6 +83,5 @@ while (argv.thrash) {
84
83
  bootstrapName = await contact.fetchBootstrap(argv.baseURL);
85
84
  bootstrapContact = await contact.ensureRemoteContact(bootstrapName, argv.baseURL);
86
85
  await contact.join(bootstrapContact);
87
- old.host.xlog('rejoined as', next);
88
86
  }
89
87
 
@@ -16,7 +16,7 @@ export { Node, Contact };
16
16
 
17
17
 
18
18
  export async function start1(name, bootstrapContact, refreshTimeIntervalMS, isServerNode = false) {
19
- const contact = await Contact.create({name, refreshTimeIntervalMS, isServerNode});
19
+ const contact = await Contact.create({name, refreshTimeIntervalMS, isServerNode, info: false});
20
20
  if (bootstrapContact) await contact.join(bootstrapContact);
21
21
  return contact;
22
22
  }
package/spec/portal.js CHANGED
@@ -79,6 +79,7 @@ const argv = yargs(hideBin(process.argv))
79
79
  if (cluster.isPrimary) { // Parent process with portal webserver through which clienta can bootstrap
80
80
  // Our job is to launch some kdht nodes to which clients can connect by signaling through
81
81
  // a little web server operated here.
82
+ console.log(`${cpus()[0].model}, ${logicalCores} logical cores.`);
82
83
  process.title = 'kdht-portal-server';
83
84
  const __filename = fileURLToPath(import.meta.url);
84
85
  const __dirname = path.dirname(__filename);
@@ -83,7 +83,7 @@ export class Contact {
83
83
  this.host.removeLooseTransport(this.key); // If any.
84
84
  }
85
85
  bye() { // The sender is disconnecting from the network
86
- this.host.log('removing disconnected contact', this.sname);
86
+ this.host.ilog('removing disconnected contact', this.sname);
87
87
  this.host.removeContact(this).then(bucket => bucket?.resetRefresh('now')); // Accelerate the bucket refresh
88
88
  }
89
89
  distance(key) { return this.host.constructor.distance(this.key, key); }
@@ -158,7 +158,6 @@ export class Contact {
158
158
  const deserialized = await this.deserializeRequest(...data);
159
159
  let response = await this.host.receiveRPC(...deserialized);
160
160
  response = this.serializeResponse(response);
161
- //if (messageTag.startsWith('X')) this.host.xlog(this.counter, 'responding', messageTag, response, 'to', this.sname);
162
161
  await this.send([messageTag, response]);
163
162
  }
164
163
  }
@@ -76,7 +76,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
76
76
  return `@${this.host.contact.sname} ==> ${this.sname}`;
77
77
  }
78
78
 
79
- ensureWebRTC(initiate = false, timeoutMS = this.host.timeoutMS || 10e3) { // Ensure we are connected, if possible.
79
+ ensureWebRTC(initiate = false, timeoutMS = this.host.timeoutMS || 30e3) { // Ensure we are connected, if possible.
80
80
  // If not already configured, sets up contact to have properties:
81
81
  // - connection - a promise for an open webrtc data channel:
82
82
  // this.send(string) puts data on the channel
@@ -118,7 +118,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
118
118
  clearTimeout(timeout);
119
119
  dataChannel.addEventListener('close', onclose);
120
120
  dataChannel.addEventListener('message', event => this.receiveWebRTC(event.data));
121
- if (this.host.debug) await webrtc.reportConnection(true); // TODO: make this asymchronous?
121
+ await webrtc.reportConnection(true);
122
122
  if (webrtc.statsElapsed > 500) this.host.xlog(`** slow connection to ${this.sname} took ${webrtc.statsElapsed.toLocaleString()} ms. **`);
123
123
  this.unsafeData = dataChannel;
124
124
  return dataChannel;
@@ -130,7 +130,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
130
130
  const timerPromise = new Promise(expired => {
131
131
  timeout = setTimeout(async () => {
132
132
  const now = Date.now();
133
- this.host.xlog('Unable to connect to', this.sname);
133
+ this.host.ilog('Unable to connect to', this.sname);
134
134
  // this.host.xlog('**** connection timeout', this.sname, now - start,
135
135
  // 'status:', webrtc.pc.connectionState, 'signaling:', webrtc.pc.signalingState,
136
136
  // 'last signal:', now - webrtc.lastOutboundSignal,
@@ -161,7 +161,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
161
161
  async send(message) { // Promise to send through previously opened connection promise.
162
162
  let channel = await this.connection;
163
163
  if (channel?.readyState === 'open') channel.send(JSON.stringify(message));
164
- else this.host.xlog('Unable to open channel');
164
+ else this.host.xlog('Tried to send on unopen channel on', this.sname, message);
165
165
  }
166
166
  synchronousSend(message) { // this.send awaits channel open promise. This is if we know it has been opened.
167
167
  if (this.unsafeData?.readyState !== 'open') return; // But it may have since been closed.
@@ -232,12 +232,6 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
232
232
  // The message could the start of an RPC sent from the peer, or it could be a response to an RPC that we made.
233
233
  // As we do the latter, we generate and note (in transmitRPC) a message tag included in the message.
234
234
  // If we find that in our messageResolvers tags, then the message is a response.
235
- if (dataString === '"bye"') { // Special messsage that the other side is disconnecting, so we can clean up early.
236
- this.webrtc.close();
237
- this.host.xlog('removing disconnected contact', this.sname);
238
- await this.host.removeContact(this); // TODO: Make sure we're not invoking this in maxTransports cases.
239
- return;
240
- }
241
235
  const [messageTag, ...data] = JSON.parse(dataString);
242
236
  await this.receiveRPC(messageTag, ...data);
243
237
  }