trac-peer 0.1.57 → 0.1.58

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "trac-peer",
3
3
  "main": "src/index.js",
4
- "version": "0.1.57",
4
+ "version": "0.1.58",
5
5
  "type": "module",
6
6
  "dependencies": {
7
7
  "assert": "npm:bare-node-assert",
@@ -37,6 +37,7 @@
37
37
  "brittle": "3.0.0",
38
38
  "buffer": "npm:bare-node-buffer",
39
39
  "child_process": "npm:bare-node-child-process",
40
+ "compact-encoding": "^2.16.1",
40
41
  "console": "npm:bare-node-console",
41
42
  "corestore": "7.4.1",
42
43
  "crypto": "npm:bare-node-crypto",
@@ -60,6 +61,7 @@
60
61
  "path": "npm:bare-node-path",
61
62
  "pear-interface": "1.0.0",
62
63
  "process": "npm:bare-node-process",
64
+ "protomux": "^3.10.1",
63
65
  "protomux-wakeup": "^2.2.1",
64
66
  "readline": "npm:bare-node-readline",
65
67
  "ready-resource": "^1.0.0",
package/src/api.js CHANGED
@@ -2,6 +2,14 @@ import b4a from "b4a";
2
2
  import {jsonStringify} from "./functions.js";
3
3
 
4
4
  export class ProtocolApi{
5
+
6
+ /**
7
+ * Exposes read and write functions.
8
+ * May be extended by contract protocol instances.
9
+ *
10
+ * @param peer
11
+ * @param options
12
+ */
5
13
  constructor(peer, options = {}) {
6
14
  this.peer = peer;
7
15
  this.api_tx_exposed = options.api_tx_exposed === true;
@@ -9,30 +17,57 @@ export class ProtocolApi{
9
17
  this.options = options;
10
18
  }
11
19
 
20
+ /**
21
+ *
22
+ * @returns {null|string}
23
+ */
12
24
  getPeerValidatorAddress(){
13
25
  return this.peer.validator;
14
26
  }
15
27
 
28
+ /**
29
+ *
30
+ * @returns {null|string}
31
+ */
16
32
  getPeerBootstrap(){
17
33
  return this.peer.bootstrap;
18
34
  }
19
35
 
36
+ /**
37
+ *
38
+ * @returns {null|string}
39
+ */
20
40
  getPeerMsbBootstrap(){
21
41
  return this.peer.msb.bootstrap;
22
42
  }
23
43
 
44
+ /**
45
+ *
46
+ * @returns {null|string}
47
+ */
24
48
  getPeerWriterKey(){
25
49
  return this.peer.writerLocalKey;
26
50
  }
27
51
 
52
+ /**
53
+ *
54
+ * @returns {string}
55
+ */
28
56
  generateNonce(){
29
57
  return this.peer.protocol_instance.generateNonce();
30
58
  }
31
59
 
32
- async prepareMessage(msg, address, reply_to = null, attachments = []){
60
+ /**
61
+ * @throws Error
62
+ * @param msg
63
+ * @param address
64
+ * @param reply_to
65
+ * @param attachments
66
+ * @returns {{dispatch: {type: string, msg: string, address, attachments: *[], deleted_by: null, reply_to: (number|null), pinned: boolean, pin_id: null}}}
67
+ */
68
+ prepareMessage(msg, address, reply_to = null, attachments = []){
33
69
  if(typeof msg !== 'string') throw new Error('Msg must be a string');
34
- if(b4a.byteLength(jsonStringify(address)) > 64) throw new Error('Address too large.');
35
- if(b4a.byteLength(jsonStringify(reply_to)) > 256) throw new Error('Reply to too large.');
70
+ if(b4a.byteLength(jsonStringify(address)) !== 66) throw new Error('Address too large.');
36
71
  if(reply_to !== null && isNaN(parseInt(reply_to))) throw new Error('Reply to not a number.');
37
72
  if(false === Array.isArray(attachments)) throw new Error('attachments must be an array.');
38
73
  if(attachments.length > 20) throw new Error('Too many attachments');
@@ -54,8 +89,36 @@ export class ProtocolApi{
54
89
  return prepared;
55
90
  }
56
91
 
92
+ /**
93
+ *
94
+ * @returns {boolean}
95
+ */
96
+ msgExposed(){
97
+ return true === this.api_msg_exposed;
98
+ }
99
+
100
+ /**
101
+ * To post a message, an ed25519 signature has to be provided over the given message + nonce.
102
+ *
103
+ * Signing steps:
104
+ *
105
+ * let address = your_ed25519_wallet.getAccountAddress()
106
+ * let nonce = api.generateNonce()
107
+ * let prepared_message = api.prepareMessage("my message", address)
108
+ * let signature = your_ed25519_wallet.sign(JSON.stringify(prepared_message) + nonce)
109
+ * let result = api.post(prepared_message, signature, nonce)
110
+ *
111
+ * Note: new messages can be read from this api's msg functions.
112
+ *
113
+ * @throws Error
114
+ * @param prepared_message
115
+ * @param signature
116
+ * @param nonce
117
+ * @returns {Promise<void>}
118
+ */
57
119
  async post(prepared_message, signature, nonce){
58
- if(false === this.api_msg_exposed) throw new Error('Posting messages not exposed in API.');
120
+ if(true !== this.api_msg_exposed) throw new Error('Posting messages not exposed in API.');
121
+ if(this.peer.base.writable === false) throw new Error('Peer is not writable.');
59
122
  if(b4a.byteLength(jsonStringify(prepared_message)) > this.peer.protocol_instance.msgMaxBytes()) throw new Error('Prepared message too large.');
60
123
  if(typeof prepared_message !== 'object') throw new Error('Prepared message must be an object generated with api.prepareMessage().');
61
124
  if(prepared_message.dispatch === undefined || prepared_message.dispatch.type === undefined ||
@@ -66,6 +129,8 @@ export class ProtocolApi{
66
129
  if(prepared_message.dispatch.type !== 'msg') throw new Error('Invalid type.');
67
130
  if(typeof prepared_message.dispatch.msg !== 'string') throw new Error('Msg must be a string');
68
131
  if(b4a.toString(b4a.from(prepared_message.dispatch.address, 'hex'), 'hex') !== prepared_message.dispatch.address) throw new Error('Invalid address.');
132
+ if(b4a.toString(b4a.from(signature, 'hex'), 'hex') !== signature) throw new Error('Invalid signature.');
133
+ if(b4a.toString(b4a.from(nonce, 'hex'), 'hex') !== nonce) throw new Error('Invalid nonce.');
69
134
  if(false === Array.isArray(prepared_message.dispatch.attachments)) throw new Error('attachments must be an array.');
70
135
  if(prepared_message.dispatch.attachments.length > 20) throw new Error('Too many attachments');
71
136
  for(let i = 0; i < prepared_message.dispatch.attachments.length; i++){
@@ -80,25 +145,52 @@ export class ProtocolApi{
80
145
  await this.peer.base.append({type: 'msg', value: prepared_message, hash : signature, nonce: nonce });
81
146
  }
82
147
 
148
+ /**
149
+ * @throws Error
150
+ * @param address
151
+ * @param command_hash
152
+ * @param nonce
153
+ * @returns {Promise<*>}
154
+ */
83
155
  async generateTx(address, command_hash, nonce) {
156
+ if(this.getPeerValidatorAddress() === null) throw new Error('Peer not connected to a validator.');
84
157
  return await this.peer.protocol_instance.generateTx(this.getPeerBootstrap(),
85
158
  this.getPeerMsbBootstrap(), this.getPeerValidatorAddress(), this.getPeerWriterKey(),
86
159
  address, command_hash, nonce);
87
160
  }
88
161
 
162
+ /**
163
+ *
164
+ * @param command
165
+ * @returns {*}
166
+ */
89
167
  prepareTxCommand(command){
90
168
  return this.peer.protocol_instance.mapTxCommand(command);
91
169
  }
92
170
 
93
171
  /**
94
- * To pass a TX, an ed25519 signature has to be provided over the given tx + nonce.
172
+ *
173
+ * @returns {boolean}
174
+ */
175
+ txExposed(){
176
+ return true === this.api_tx_exposed;
177
+ }
178
+
179
+ /**
180
+ * To broadcast a TX, an ed25519 signature has to be provided over the given tx + nonce.
95
181
  *
96
182
  * Signing steps:
183
+ *
184
+ * let address = your_ed25519_wallet.getAccountAddress()
97
185
  * let nonce = api.generateNonce()
98
186
  * let tx_hash = api.generateTx(address, a_sha256_function(JSON.stringify(api.prepareTxCommand(command))), nonce)
99
- * let signature = your_ed25519_sign_lib.sign(tx + nonce, address)
100
- * api.tx(tx_hash, api.prepareTxCommand(command), address, signature, nonce)
187
+ * let signature = your_ed25519_wallet.sign(tx + nonce)
188
+ * // simulating
189
+ * let sim_result = api.tx(tx_hash, api.prepareTxCommand(command), address, signature, nonce, true)
190
+ * // broadcasting
191
+ * let result = api.tx(tx_hash, api.prepareTxCommand(command), address, signature, nonce)
101
192
  *
193
+ * @throws Error
102
194
  * @param tx
103
195
  * @param prepared_command
104
196
  * @param address
@@ -108,18 +200,19 @@ export class ProtocolApi{
108
200
  * @returns {Promise<boolean|object>}
109
201
  */
110
202
  async tx(tx, prepared_command, address, signature, nonce, sim = false ){
111
- if(false === this.api_tx_exposed) throw new Error('Transactions not exposed in API.');
203
+ if(true !== this.api_tx_exposed) throw new Error('Transactions not exposed in API.');
204
+ if(this.peer.base.writable === false) throw new Error('Peer is not writable.');
112
205
  if(this.getPeerValidatorAddress() === null) throw new Error('Peer not connected to a validator.');
113
206
  if(typeof prepared_command !== 'object') throw new Error('prepared_command must be an object.');
114
207
  if(typeof prepared_command.type !== 'string') throw new Error('prepared_command.type must exist and be a string.');
115
208
  if(prepared_command.value === undefined) throw new Error('prepared_command.value is missing.');
116
209
  if(b4a.byteLength(jsonStringify(prepared_command)) > this.peer.protocol_instance.txMaxBytes()) throw new Error('prepared_command too large.');
117
- if(b4a.byteLength(jsonStringify(address)) !== 64) throw new Error('Address length invalid.');
118
- if(b4a.byteLength(jsonStringify(signature)) !== 128) throw new Error('Signature length invalid.');
119
- if(b4a.byteLength(jsonStringify(nonce)) !== 64) throw new Error('Nonce length invalid.');
120
- if(b4a.toString(b4a.from(signature, 'utf8'), 'hex') !== signature) throw new Error('Invalid signature.');
121
- if(b4a.toString(b4a.from(address, 'utf8'), 'hex') !== address) throw new Error('Invalid address.');
122
- if(b4a.toString(b4a.from(nonce, 'utf8'), 'hex') !== nonce) throw new Error('Invalid nonce.');
210
+ if(b4a.byteLength(jsonStringify(address)) !== 66) throw new Error('Address length invalid.');
211
+ if(b4a.byteLength(jsonStringify(signature)) !== 130) throw new Error('Signature length invalid.');
212
+ if(b4a.byteLength(jsonStringify(nonce)) !== 66) throw new Error('Nonce length invalid.');
213
+ if(b4a.toString(b4a.from(address, 'hex'), 'hex') !== address) throw new Error('Invalid address.');
214
+ if(b4a.toString(b4a.from(signature, 'hex'), 'hex') !== signature) throw new Error('Invalid signature.');
215
+ if(b4a.toString(b4a.from(nonce, 'hex'), 'hex') !== nonce) throw new Error('Invalid nonce.');
123
216
  const verified = this.peer.wallet.verify(signature, tx + nonce, address);
124
217
  if(false === verified) throw new Error('Invalid signature.');
125
218
  const content_hash = await this.peer.createHash('sha256', this.peer.protocol_instance.safeJsonStringify(prepared_command));
@@ -140,6 +233,11 @@ export class ProtocolApi{
140
233
  return res;
141
234
  }
142
235
 
236
+ /**
237
+ *
238
+ * @param signed
239
+ * @returns {Promise<null>}
240
+ */
143
241
  async getAdmin(signed = true){
144
242
  let res = null;
145
243
  if(true === signed) res = await this.peer.protocol_instance.getSigned('admin');
@@ -148,6 +246,11 @@ export class ProtocolApi{
148
246
  return null;
149
247
  }
150
248
 
249
+ /**
250
+ *
251
+ * @param signed
252
+ * @returns {Promise<boolean|null>}
253
+ */
151
254
  async getWhitelistEnabled(signed = true){
152
255
  let res = null;
153
256
  if(true === signed) res = await this.peer.protocol_instance.getSigned('wlst');
@@ -156,6 +259,12 @@ export class ProtocolApi{
156
259
  return false;
157
260
  }
158
261
 
262
+ /**
263
+ *
264
+ * @param address
265
+ * @param signed
266
+ * @returns {Promise<boolean|null>}
267
+ */
159
268
  async getWhitelistStatus(address, signed = true){
160
269
  let res = null;
161
270
  if(true === signed) res = await this.peer.protocol_instance.getSigned('wl/'+address);
@@ -164,6 +273,12 @@ export class ProtocolApi{
164
273
  return false;
165
274
  }
166
275
 
276
+ /**
277
+ *
278
+ * @param address
279
+ * @param signed
280
+ * @returns {Promise<boolean|null>}
281
+ */
167
282
  async getModStatus(address, signed = true){
168
283
  let res = null;
169
284
  if(true === signed) res = await this.peer.protocol_instance.getSigned('mod/'+address);
@@ -172,6 +287,12 @@ export class ProtocolApi{
172
287
  return false;
173
288
  }
174
289
 
290
+ /**
291
+ *
292
+ * @param address
293
+ * @param signed
294
+ * @returns {Promise<boolean|null>}
295
+ */
175
296
  async getMuteStatus(address, signed = true){
176
297
  let res = null;
177
298
  if(true === signed) res = await this.peer.protocol_instance.getSigned('mtd/'+address);
@@ -180,6 +301,11 @@ export class ProtocolApi{
180
301
  return false;
181
302
  }
182
303
 
304
+ /**
305
+ *
306
+ * @param signed
307
+ * @returns {Promise<boolean>}
308
+ */
183
309
  async getAutoAddWritersStatus(signed = true){
184
310
  let res = null;
185
311
  if(true === signed) res = await this.peer.protocol_instance.getSigned('auto_add_writers');
@@ -190,6 +316,11 @@ export class ProtocolApi{
190
316
  return false;
191
317
  }
192
318
 
319
+ /**
320
+ *
321
+ * @param signed
322
+ * @returns {Promise<boolean>}
323
+ */
193
324
  async getChatStatus(signed = true){
194
325
  let res = null;
195
326
  if(true === signed) res = await this.peer.protocol_instance.getSigned('chat_status');
@@ -200,6 +331,12 @@ export class ProtocolApi{
200
331
  return false;
201
332
  }
202
333
 
334
+ /**
335
+ *
336
+ * @param nick
337
+ * @param signed
338
+ * @returns {Promise<null>}
339
+ */
203
340
  async getAddressFromNick(nick, signed = true){
204
341
  let res = null;
205
342
  if(true === signed) res = await this.peer.protocol_instance.getSigned('kcin/'+nick);
@@ -207,6 +344,12 @@ export class ProtocolApi{
207
344
  return res;
208
345
  }
209
346
 
347
+ /**
348
+ *
349
+ * @param address
350
+ * @param signed
351
+ * @returns {Promise<null>}
352
+ */
210
353
  async getNick(address, signed = true){
211
354
  let res = null;
212
355
  if(true === signed) res = await this.peer.protocol_instance.getSigned('nick/'+address);
@@ -214,6 +357,11 @@ export class ProtocolApi{
214
357
  return res;
215
358
  }
216
359
 
360
+ /**
361
+ *
362
+ * @param signed
363
+ * @returns {Promise<number>}
364
+ */
217
365
  async getPinnedMessageLength(signed = true){
218
366
  let res = null;
219
367
  if(true === signed) res = await this.peer.protocol_instance.getSigned('pnl');
@@ -222,6 +370,12 @@ export class ProtocolApi{
222
370
  return res;
223
371
  }
224
372
 
373
+ /**
374
+ *
375
+ * @param index
376
+ * @param signed
377
+ * @returns {Promise<null>}
378
+ */
225
379
  async getPinnedMessage(index, signed = true){
226
380
  let res = null;
227
381
  if(true === signed) res = await this.peer.protocol_instance.getSigned('pni/'+parseInt(index));
@@ -232,6 +386,11 @@ export class ProtocolApi{
232
386
  return await this.getMessage(res.msg, signed);
233
387
  }
234
388
 
389
+ /**
390
+ *
391
+ * @param signed
392
+ * @returns {Promise<number>}
393
+ */
235
394
  async getDeletedMessageLength(signed = true){
236
395
  let res = null;
237
396
  if(true === signed) res = await this.peer.protocol_instance.getSigned('delml');
@@ -240,6 +399,12 @@ export class ProtocolApi{
240
399
  return res;
241
400
  }
242
401
 
402
+ /**
403
+ *
404
+ * @param index
405
+ * @param signed
406
+ * @returns {Promise<null>}
407
+ */
243
408
  async getDeletedMessage(index, signed = true){
244
409
  let res = null;
245
410
  if(true === signed) res = await this.peer.protocol_instance.getSigned('delm/'+parseInt(index));
@@ -250,6 +415,11 @@ export class ProtocolApi{
250
415
  return await this.getMessage(res, signed);
251
416
  }
252
417
 
418
+ /**
419
+ *
420
+ * @param signed
421
+ * @returns {Promise<number>}
422
+ */
253
423
  async getMessageLength(signed = true){
254
424
  let res = null;
255
425
  if(true === signed) res = await this.peer.protocol_instance.getSigned('msgl');
@@ -258,6 +428,12 @@ export class ProtocolApi{
258
428
  return res;
259
429
  }
260
430
 
431
+ /**
432
+ *
433
+ * @param index
434
+ * @param signed
435
+ * @returns {Promise<null>}
436
+ */
261
437
  async getMessage(index, signed = true){
262
438
  let res = null;
263
439
  if(true === signed) res = await this.peer.protocol_instance.getSigned('msg/'+parseInt(index));
@@ -265,6 +441,12 @@ export class ProtocolApi{
265
441
  return res;
266
442
  }
267
443
 
444
+ /**
445
+ *
446
+ * @param address
447
+ * @param signed
448
+ * @returns {Promise<number>}
449
+ */
268
450
  async getUserMessageLength(address, signed = true){
269
451
  let res = null;
270
452
  if(true === signed) res = await this.peer.protocol_instance.getSigned('umsgl/'+address);
@@ -273,6 +455,13 @@ export class ProtocolApi{
273
455
  return res;
274
456
  }
275
457
 
458
+ /**
459
+ *
460
+ * @param address
461
+ * @param index
462
+ * @param signed
463
+ * @returns {Promise<null>}
464
+ */
276
465
  async getUserMessage(address, index, signed = true){
277
466
  let res = null;
278
467
  if(true === signed) res = await this.peer.protocol_instance.getSigned('umsg/'+address+'/'+parseInt(index));
@@ -283,6 +472,11 @@ export class ProtocolApi{
283
472
  return await this.getMessage(res, signed);
284
473
  }
285
474
 
475
+ /**
476
+ *
477
+ * @param signed
478
+ * @returns {Promise<number>}
479
+ */
286
480
  async getTxLength(signed = true){
287
481
  let res = null;
288
482
  if(true === signed) res = await this.peer.protocol_instance.getSigned('txl');
@@ -291,6 +485,12 @@ export class ProtocolApi{
291
485
  return res;
292
486
  }
293
487
 
488
+ /**
489
+ *
490
+ * @param index
491
+ * @param signed
492
+ * @returns {Promise<null>}
493
+ */
294
494
  async getTx(index, signed = true){
295
495
  let res = null;
296
496
  if(true === signed) res = await this.peer.protocol_instance.getSigned('txi/'+parseInt(index));
@@ -298,6 +498,12 @@ export class ProtocolApi{
298
498
  return res;
299
499
  }
300
500
 
501
+ /**
502
+ *
503
+ * @param tx
504
+ * @param signed
505
+ * @returns {Promise<null>}
506
+ */
301
507
  async getTxData(tx, signed = true){
302
508
  let index = null;
303
509
  if(true === signed) index = await this.peer.protocol_instance.getSigned('tx/'+tx);
@@ -308,6 +514,12 @@ export class ProtocolApi{
308
514
  return null;
309
515
  }
310
516
 
517
+ /**
518
+ *
519
+ * @param address
520
+ * @param signed
521
+ * @returns {Promise<number>}
522
+ */
311
523
  async getUserTxLength(address, signed = true){
312
524
  let res = null;
313
525
  if(true === signed) res = await this.peer.protocol_instance.getSigned('utxl/'+address);
@@ -316,6 +528,13 @@ export class ProtocolApi{
316
528
  return res;
317
529
  }
318
530
 
531
+ /**
532
+ *
533
+ * @param address
534
+ * @param index
535
+ * @param signed
536
+ * @returns {Promise<null>}
537
+ */
319
538
  async getUserTx(address, index, signed = true){
320
539
  let res = null;
321
540
  if(true === signed) res = await this.peer.protocol_instance.getSigned('utxi/'+address+'/'+parseInt(index));
package/src/functions.js CHANGED
@@ -341,40 +341,7 @@ export async function joinValidator(input, peer){
341
341
  if(validator === null || false === validator.value.isWriter || true === validator.value.isIndexer){
342
342
  throw new Error('Invalid validator address.');
343
343
  }
344
- const result = await peer.isValidatorAvailable(address);
345
- if(result === null) {
346
- console.log('Validator not available');
347
- } else {
348
- let existing_stream = undefined;
349
- if(peer.msb.getSwarm().peers.has(address)){
350
- const peerInfo = peer.msb.getSwarm().peers.get(address)
351
- existing_stream = peer.msb.getSwarm()._allConnections.get(peerInfo.publicKey)
352
- }
353
- if(existing_stream !== undefined){
354
- peer.validator_stream = existing_stream;
355
- peer.validator = address;
356
- peer.validator_stream.on('close', () => {
357
- peer.validator_stream = null;
358
- peer.validator = null;
359
- console.log('Validator stream closed', address);
360
- });
361
- console.log('Validator stream established', address);
362
- } else {
363
- peer.validator_stream = peer.msb.getSwarm().dht.connect(b4a.from(address, 'hex'));
364
- peer.validator = address;
365
- peer.validator_stream.on('open', () => {
366
- console.log('Validator stream established', address);
367
- });
368
- peer.validator_stream.on('close', () => {
369
- peer.validator_stream = null;
370
- peer.validator = null;
371
- });
372
- peer.validator_stream.on('error', (error) => {
373
- peer.validator_stream = null;
374
- peer.validator = null;
375
- });
376
- }
377
- }
344
+ await peer.tryConnection(address);
378
345
  }
379
346
 
380
347
  export async function tx(input, peer){
package/src/index.js CHANGED
@@ -11,6 +11,8 @@ import Corestore from 'corestore';
11
11
  import w from 'protomux-wakeup';
12
12
  import BlindPairing from 'blind-pairing'
13
13
  const wakeup = new w();
14
+ import Protomux from 'protomux'
15
+ import c from 'compact-encoding'
14
16
  import {
15
17
  addWriter, addAdmin, setAutoAddWriters, setChatStatus, setMod, deleteMessage,
16
18
  enableWhitelist, postMessage, jsonStringify, visibleLength, setNick,
@@ -44,6 +46,7 @@ export class Peer extends ReadyResource {
44
46
  this.contract = options.contract || null;
45
47
  this.wallet = options.wallet || null;
46
48
  this.features = options.features || [];
49
+ this.custom_validators = options.custom_validators || [];
47
50
  this.protocol_instance = null;
48
51
  this.contract_instance = null;
49
52
  this.channel = b4a.alloc(32).fill(options.channel) || null;
@@ -512,6 +515,36 @@ export class Peer extends ReadyResource {
512
515
  try{ await this.validator_stream.send(b4a.from(jsonStringify(_msg))); } catch(e){ }
513
516
  }
514
517
 
518
+ async tryConnection(address){
519
+ if(null === this.swarm) return null;
520
+
521
+ // trying to join a peer from the global swarm
522
+ if(false === this.swarm.peers.has(address)){
523
+ this.swarm.joinPeer(b4a.from(address, 'hex'));
524
+ let cnt = 0;
525
+ while(false === this.swarm.peers.has(address)){
526
+ if(cnt >= 15) break;
527
+ await sleep(1_000);
528
+ cnt += 1;
529
+ }
530
+ }
531
+
532
+ if(this.swarm.peers.has(address)){
533
+ let stream;
534
+ const peerInfo = this.swarm.peers.get(address)
535
+ stream = this.swarm._allConnections.get(peerInfo.publicKey)
536
+ if(stream !== undefined && stream.messenger !== undefined){
537
+ stream.messenger.send('get_validator');
538
+ let cnt = 0;
539
+ while(this.validator_stream === null){
540
+ if(cnt >= 1500) break;
541
+ await this.sleep(10);
542
+ cnt += 1;
543
+ }
544
+ }
545
+ }
546
+ }
547
+
515
548
  async isValidatorAvailable(address){
516
549
  if(null === this.msb.getSwarm()) return null;
517
550
  let writer_key = null;
@@ -529,7 +562,9 @@ export class Peer extends ReadyResource {
529
562
  existing_stream.on('message', (msg) => {
530
563
  try{
531
564
  const response = jsonParse(b4a.toString(msg, 'utf-8'));
532
- if(response.op === 'writer_key' && response.key !== undefined){
565
+ if(response.op === 'writer_key' && response.key !== undefined &&
566
+ response.channel !== undefined &&
567
+ response.channel === b4a.toString(this.msb.getChannel(), 'utf8')){
533
568
  writer_key = response.key;
534
569
  }
535
570
  }catch(e){}
@@ -553,7 +588,9 @@ export class Peer extends ReadyResource {
553
588
  stream.on('message', (msg) => {
554
589
  try{
555
590
  const response = jsonParse(b4a.toString(msg, 'utf-8'));
556
- if(response.op === 'writer_key' && response.key !== undefined){
591
+ if(response.op === 'writer_key' && response.key !== undefined &&
592
+ response.channel !== undefined &&
593
+ response.channel === b4a.toString(this.msb.getChannel(), 'utf8')){
557
594
  writer_key = response.key;
558
595
  }
559
596
  }catch(e){}
@@ -587,25 +624,6 @@ export class Peer extends ReadyResource {
587
624
  }
588
625
  });
589
626
  this.updater();
590
-
591
- // test
592
- const pins = this.protocol_instance;
593
- const command = '';
594
-
595
- const prepared_command = pins.api.prepareTxCommand(command);
596
- let nonce = pins.api.generateNonce();
597
-
598
- let tx_hash = pins.api.generateTx(
599
- this.wallet.publicKey, await this.createHash('sha256', JSON.stringify(prepared_command)), nonce
600
- )
601
-
602
- console.log('TX Hash', tx_hash)
603
-
604
- const signature = this.wallet.sign(tx_hash + nonce)
605
-
606
- const res = await pins.api.tx(tx_hash, prepared_command, this.wallet.publicKey, signature, nonce)
607
-
608
- console.log('TX Res', res)
609
627
  }
610
628
 
611
629
  async initContract(){
@@ -642,50 +660,23 @@ export class Peer extends ReadyResource {
642
660
  } else {
643
661
  length = length.value;
644
662
  }
663
+ if(this.custom_validators.length !== 0){
664
+ length = this.custom_validators.length;
665
+ }
645
666
  async function findSome(){
646
667
  if(_this.validator_stream !== null) return;
647
668
  const rnd_index = Math.floor(Math.random() * length);
648
669
  let validator = await _this.msb.base.view.get('wri/' + rnd_index);
670
+ if(_this.custom_validators.length !== 0){
671
+ validator = { value : _this.custom_validators[rnd_index] };
672
+ console.log('Trying custom validator', validator.value);
673
+ }
649
674
  if(_this.validator_stream !== null) return;
650
675
  if (null !== validator) {
651
676
  validator = await _this.msb.base.view.get(validator.value);
652
677
  if(_this.validator_stream !== null) return;
653
678
  if(null !== validator && false !== validator.value.isWriter && false === validator.value.isIndexer) {
654
- const result = await _this.isValidatorAvailable(validator.value.pub);
655
- if(_this.validator_stream !== null) return;
656
- if (null !== result) {
657
- await _this.sleep(100);
658
- if(_this.validator_stream !== null) return;
659
- let existing_stream = undefined;
660
- if(_this.msb.getSwarm().peers.has(validator.value.pub)){
661
- const peerInfo = _this.msb.getSwarm().peers.get(validator.value.pub)
662
- existing_stream = _this.msb.getSwarm()._allConnections.get(peerInfo.publicKey)
663
- }
664
- if(existing_stream !== undefined){
665
- _this.validator_stream = existing_stream;
666
- _this.validator = validator.value.pub;
667
- _this.validator_stream.on('close', () => {
668
- _this.validator_stream = null;
669
- _this.validator = null;
670
- console.log('Validator stream closed', validator.value.pub);
671
- });
672
- console.log('Validator stream established', validator.value.pub);
673
- } else {
674
- _this.validator_stream = _this.msb.getSwarm().dht.connect(b4a.from(validator.value.pub, 'hex'));
675
- _this.validator = validator.value.pub;
676
- _this.validator_stream.on('open', () => {
677
- console.log('Validator stream established', validator.value.pub);
678
- });
679
- _this.validator_stream.on('close', () => {
680
- _this.validator_stream = null;
681
- _this.validator = null;
682
- });
683
- _this.validator_stream.on('error', (error) => {
684
- _this.validator_stream = null;
685
- _this.validator = null;
686
- });
687
- }
688
- }
679
+ await _this.tryConnection(validator.value.pub);
689
680
  }
690
681
  }
691
682
  }
@@ -786,6 +777,40 @@ export class Peer extends ReadyResource {
786
777
  console.log('');
787
778
 
788
779
  this.swarm.on('connection', async (connection, peerInfo) => {
780
+
781
+ const mux = Protomux.from(connection)
782
+ connection.userData = mux
783
+
784
+ const message_channel = mux.createChannel({
785
+ protocol: b4a.toString(channel, 'utf8'),
786
+ onopen() { },
787
+ onclose() { }
788
+ })
789
+
790
+ message_channel.open()
791
+
792
+ const message = message_channel.addMessage({
793
+ encoding: c.json,
794
+ async onmessage(msg) {
795
+ try{
796
+ if(msg.response !== undefined && msg.response.op !== undefined && msg.response.op === 'validator'){
797
+ const res = await _this.msb.get(msg.response.address);
798
+ if(res === null) return;
799
+ const verified = _this.wallet.verify(msg.sig, JSON.stringify(msg.response) + msg.nonce, msg.response.address)
800
+ if(verified && msg.response.channel === b4a.toString(channel, 'utf8')){
801
+ console.log('Validator stream established', msg.response.address)
802
+ _this.validator_stream = connection;
803
+ _this.validator = msg.response.address;
804
+ }
805
+ }
806
+ } catch (e) {
807
+ console.log(e);
808
+ }
809
+ }
810
+ })
811
+
812
+ connection.messenger = message;
813
+
789
814
  const remotePublicKey = b4a.toString(connection.remotePublicKey, 'hex');
790
815
 
791
816
  this.connectedPeers.add(remotePublicKey);
@@ -794,6 +819,11 @@ export class Peer extends ReadyResource {
794
819
  this.connectedNodes++;
795
820
 
796
821
  connection.on('close', () => {
822
+ if(this.validator_stream === connection){
823
+ this.validator_stream = null;
824
+ this.validator = null;
825
+ }
826
+ message_channel.close()
797
827
  this.connectedNodes--;
798
828
  this.connectedPeers.delete(remotePublicKey);
799
829
  });
package/src/protocol.js CHANGED
@@ -192,7 +192,7 @@ class Protocol{
192
192
  async tx(subject, sim = false, surrogate = null){
193
193
  if(this.peer.validator_stream === null) throw new Error('HyperMallProtocol::tx(): No validator available.');
194
194
  const obj = this.mapTxCommand(subject.command);
195
- if(null !== obj) {
195
+ if(null !== obj && typeof obj.type === 'string' && obj.value !== undefined) {
196
196
  return await this.broadcastTransaction(this.peer.validator,{
197
197
  type : obj.type,
198
198
  value : obj.value