trac-peer 0.1.56 → 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.56",
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,69 +145,99 @@ 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
105
197
  * @param signature
106
198
  * @param nonce
107
199
  * @param sim
108
- * @returns {Promise<boolean>}
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.');
205
+ if(this.getPeerValidatorAddress() === null) throw new Error('Peer not connected to a validator.');
112
206
  if(typeof prepared_command !== 'object') throw new Error('prepared_command must be an object.');
113
- if(typeof prepared_command.type !== 'string') throw new Error('prepared_command.type must exist and be a string.');
114
- if(prepared_command.value === undefined) throw new Error('prepared_command.value is missing.');
207
+ if(typeof prepared_command.type !== 'string') throw new Error('prepared_command.type must exist and be a string.');
208
+ if(prepared_command.value === undefined) throw new Error('prepared_command.value is missing.');
115
209
  if(b4a.byteLength(jsonStringify(prepared_command)) > this.peer.protocol_instance.txMaxBytes()) throw new Error('prepared_command too large.');
116
- if(b4a.byteLength(jsonStringify(address)) > 64) throw new Error('Address too large.');
117
- if(b4a.byteLength(jsonStringify(signature)) > 128) throw new Error('Signature too large.');
118
- if(b4a.byteLength(jsonStringify(nonce)) > 256) throw new Error('Nonce too large.');
119
- if(b4a.toString(b4a.from(signature, 'hex'), 'hex') !== signature) throw new Error('Invalid signature.');
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.');
120
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.');
121
216
  const verified = this.peer.wallet.verify(signature, tx + nonce, address);
122
217
  if(false === verified) throw new Error('Invalid signature.');
123
218
  const content_hash = await this.peer.createHash('sha256', this.peer.protocol_instance.safeJsonStringify(prepared_command));
124
219
  let _tx = await this.generateTx(address, content_hash, nonce);
125
220
  if(tx !== _tx) throw new Error('Invalid TX.');
126
- while(null !== this.peer.protocol_instance.surrogate_tx) await this.peer.sleep(3);
127
- this.peer.protocol_instance.surrogate_tx = { tx : ''+tx, nonce : ''+nonce, signature : ''+signature, address : ''+address };
221
+ const surrogate = { tx : _tx, nonce : ''+nonce, signature : ''+signature, address : ''+address };
128
222
  let res = false;
129
223
  try{
130
- if(true === sim){
131
- while(true === this.peer.protocol_instance.sim) await this.peer.sleep(3);
132
- this.peer.protocol_instance.sim = true;
133
- }
134
- const subject = { command : prepared_command.value, validator : ''+this.getPeerValidatorAddress() };
135
- res = await this.peer.protocol_instance.tx(subject);
224
+ const subject = { command : prepared_command.value, validator : this.getPeerValidatorAddress() };
225
+ res = await this.peer.protocol_instance.tx(subject, sim === true, surrogate);
136
226
  } catch(e){ console.log(e) }
137
- const err = this.peer.protocol_instance.getError(res);
138
- if(null !== err){
139
- console.log(err.message);
227
+ if(res !== false) {
228
+ const err = this.peer.protocol_instance.getError(res);
229
+ if (null !== err) {
230
+ console.log(err.message);
231
+ }
140
232
  }
141
- this.peer.protocol_instance.sim = false;
142
- this.peer.protocol_instance.surrogate_tx = null;
143
233
  return res;
144
234
  }
145
235
 
236
+ /**
237
+ *
238
+ * @param signed
239
+ * @returns {Promise<null>}
240
+ */
146
241
  async getAdmin(signed = true){
147
242
  let res = null;
148
243
  if(true === signed) res = await this.peer.protocol_instance.getSigned('admin');
@@ -151,6 +246,11 @@ export class ProtocolApi{
151
246
  return null;
152
247
  }
153
248
 
249
+ /**
250
+ *
251
+ * @param signed
252
+ * @returns {Promise<boolean|null>}
253
+ */
154
254
  async getWhitelistEnabled(signed = true){
155
255
  let res = null;
156
256
  if(true === signed) res = await this.peer.protocol_instance.getSigned('wlst');
@@ -159,6 +259,12 @@ export class ProtocolApi{
159
259
  return false;
160
260
  }
161
261
 
262
+ /**
263
+ *
264
+ * @param address
265
+ * @param signed
266
+ * @returns {Promise<boolean|null>}
267
+ */
162
268
  async getWhitelistStatus(address, signed = true){
163
269
  let res = null;
164
270
  if(true === signed) res = await this.peer.protocol_instance.getSigned('wl/'+address);
@@ -167,6 +273,12 @@ export class ProtocolApi{
167
273
  return false;
168
274
  }
169
275
 
276
+ /**
277
+ *
278
+ * @param address
279
+ * @param signed
280
+ * @returns {Promise<boolean|null>}
281
+ */
170
282
  async getModStatus(address, signed = true){
171
283
  let res = null;
172
284
  if(true === signed) res = await this.peer.protocol_instance.getSigned('mod/'+address);
@@ -175,6 +287,12 @@ export class ProtocolApi{
175
287
  return false;
176
288
  }
177
289
 
290
+ /**
291
+ *
292
+ * @param address
293
+ * @param signed
294
+ * @returns {Promise<boolean|null>}
295
+ */
178
296
  async getMuteStatus(address, signed = true){
179
297
  let res = null;
180
298
  if(true === signed) res = await this.peer.protocol_instance.getSigned('mtd/'+address);
@@ -183,6 +301,11 @@ export class ProtocolApi{
183
301
  return false;
184
302
  }
185
303
 
304
+ /**
305
+ *
306
+ * @param signed
307
+ * @returns {Promise<boolean>}
308
+ */
186
309
  async getAutoAddWritersStatus(signed = true){
187
310
  let res = null;
188
311
  if(true === signed) res = await this.peer.protocol_instance.getSigned('auto_add_writers');
@@ -193,6 +316,11 @@ export class ProtocolApi{
193
316
  return false;
194
317
  }
195
318
 
319
+ /**
320
+ *
321
+ * @param signed
322
+ * @returns {Promise<boolean>}
323
+ */
196
324
  async getChatStatus(signed = true){
197
325
  let res = null;
198
326
  if(true === signed) res = await this.peer.protocol_instance.getSigned('chat_status');
@@ -203,6 +331,12 @@ export class ProtocolApi{
203
331
  return false;
204
332
  }
205
333
 
334
+ /**
335
+ *
336
+ * @param nick
337
+ * @param signed
338
+ * @returns {Promise<null>}
339
+ */
206
340
  async getAddressFromNick(nick, signed = true){
207
341
  let res = null;
208
342
  if(true === signed) res = await this.peer.protocol_instance.getSigned('kcin/'+nick);
@@ -210,6 +344,12 @@ export class ProtocolApi{
210
344
  return res;
211
345
  }
212
346
 
347
+ /**
348
+ *
349
+ * @param address
350
+ * @param signed
351
+ * @returns {Promise<null>}
352
+ */
213
353
  async getNick(address, signed = true){
214
354
  let res = null;
215
355
  if(true === signed) res = await this.peer.protocol_instance.getSigned('nick/'+address);
@@ -217,6 +357,11 @@ export class ProtocolApi{
217
357
  return res;
218
358
  }
219
359
 
360
+ /**
361
+ *
362
+ * @param signed
363
+ * @returns {Promise<number>}
364
+ */
220
365
  async getPinnedMessageLength(signed = true){
221
366
  let res = null;
222
367
  if(true === signed) res = await this.peer.protocol_instance.getSigned('pnl');
@@ -225,6 +370,12 @@ export class ProtocolApi{
225
370
  return res;
226
371
  }
227
372
 
373
+ /**
374
+ *
375
+ * @param index
376
+ * @param signed
377
+ * @returns {Promise<null>}
378
+ */
228
379
  async getPinnedMessage(index, signed = true){
229
380
  let res = null;
230
381
  if(true === signed) res = await this.peer.protocol_instance.getSigned('pni/'+parseInt(index));
@@ -235,6 +386,11 @@ export class ProtocolApi{
235
386
  return await this.getMessage(res.msg, signed);
236
387
  }
237
388
 
389
+ /**
390
+ *
391
+ * @param signed
392
+ * @returns {Promise<number>}
393
+ */
238
394
  async getDeletedMessageLength(signed = true){
239
395
  let res = null;
240
396
  if(true === signed) res = await this.peer.protocol_instance.getSigned('delml');
@@ -243,6 +399,12 @@ export class ProtocolApi{
243
399
  return res;
244
400
  }
245
401
 
402
+ /**
403
+ *
404
+ * @param index
405
+ * @param signed
406
+ * @returns {Promise<null>}
407
+ */
246
408
  async getDeletedMessage(index, signed = true){
247
409
  let res = null;
248
410
  if(true === signed) res = await this.peer.protocol_instance.getSigned('delm/'+parseInt(index));
@@ -253,6 +415,11 @@ export class ProtocolApi{
253
415
  return await this.getMessage(res, signed);
254
416
  }
255
417
 
418
+ /**
419
+ *
420
+ * @param signed
421
+ * @returns {Promise<number>}
422
+ */
256
423
  async getMessageLength(signed = true){
257
424
  let res = null;
258
425
  if(true === signed) res = await this.peer.protocol_instance.getSigned('msgl');
@@ -261,6 +428,12 @@ export class ProtocolApi{
261
428
  return res;
262
429
  }
263
430
 
431
+ /**
432
+ *
433
+ * @param index
434
+ * @param signed
435
+ * @returns {Promise<null>}
436
+ */
264
437
  async getMessage(index, signed = true){
265
438
  let res = null;
266
439
  if(true === signed) res = await this.peer.protocol_instance.getSigned('msg/'+parseInt(index));
@@ -268,6 +441,12 @@ export class ProtocolApi{
268
441
  return res;
269
442
  }
270
443
 
444
+ /**
445
+ *
446
+ * @param address
447
+ * @param signed
448
+ * @returns {Promise<number>}
449
+ */
271
450
  async getUserMessageLength(address, signed = true){
272
451
  let res = null;
273
452
  if(true === signed) res = await this.peer.protocol_instance.getSigned('umsgl/'+address);
@@ -276,6 +455,13 @@ export class ProtocolApi{
276
455
  return res;
277
456
  }
278
457
 
458
+ /**
459
+ *
460
+ * @param address
461
+ * @param index
462
+ * @param signed
463
+ * @returns {Promise<null>}
464
+ */
279
465
  async getUserMessage(address, index, signed = true){
280
466
  let res = null;
281
467
  if(true === signed) res = await this.peer.protocol_instance.getSigned('umsg/'+address+'/'+parseInt(index));
@@ -286,6 +472,11 @@ export class ProtocolApi{
286
472
  return await this.getMessage(res, signed);
287
473
  }
288
474
 
475
+ /**
476
+ *
477
+ * @param signed
478
+ * @returns {Promise<number>}
479
+ */
289
480
  async getTxLength(signed = true){
290
481
  let res = null;
291
482
  if(true === signed) res = await this.peer.protocol_instance.getSigned('txl');
@@ -294,6 +485,12 @@ export class ProtocolApi{
294
485
  return res;
295
486
  }
296
487
 
488
+ /**
489
+ *
490
+ * @param index
491
+ * @param signed
492
+ * @returns {Promise<null>}
493
+ */
297
494
  async getTx(index, signed = true){
298
495
  let res = null;
299
496
  if(true === signed) res = await this.peer.protocol_instance.getSigned('txi/'+parseInt(index));
@@ -301,6 +498,12 @@ export class ProtocolApi{
301
498
  return res;
302
499
  }
303
500
 
501
+ /**
502
+ *
503
+ * @param tx
504
+ * @param signed
505
+ * @returns {Promise<null>}
506
+ */
304
507
  async getTxData(tx, signed = true){
305
508
  let index = null;
306
509
  if(true === signed) index = await this.peer.protocol_instance.getSigned('tx/'+tx);
@@ -311,6 +514,12 @@ export class ProtocolApi{
311
514
  return null;
312
515
  }
313
516
 
517
+ /**
518
+ *
519
+ * @param address
520
+ * @param signed
521
+ * @returns {Promise<number>}
522
+ */
314
523
  async getUserTxLength(address, signed = true){
315
524
  let res = null;
316
525
  if(true === signed) res = await this.peer.protocol_instance.getSigned('utxl/'+address);
@@ -319,6 +528,13 @@ export class ProtocolApi{
319
528
  return res;
320
529
  }
321
530
 
531
+ /**
532
+ *
533
+ * @param address
534
+ * @param index
535
+ * @param signed
536
+ * @returns {Promise<null>}
537
+ */
322
538
  async getUserTx(address, index, signed = true){
323
539
  let res = null;
324
540
  if(true === signed) res = await this.peer.protocol_instance.getSigned('utxi/'+address+'/'+parseInt(index));
package/src/feature.js CHANGED
@@ -20,18 +20,22 @@ class Feature {
20
20
  }
21
21
 
22
22
  async append(key, value){
23
- const nonce = this.peer.protocol_instance.generateNonce();
24
- const hash = this.peer.wallet.sign(JSON.stringify(value) + nonce);
25
- await this.peer.base.append({ type: 'feature', key: this.key + '_' + key, value : {
26
- dispatch : {
27
- type : this.key + '_feature',
28
- key : key,
29
- hash : hash,
30
- value : value,
31
- nonce : nonce,
32
- address : this.peer.wallet.publicKey
33
- }
34
- }});
23
+ if(this.peer.base.writable){
24
+ const nonce = this.peer.protocol_instance.generateNonce();
25
+ const hash = this.peer.wallet.sign(JSON.stringify(value) + nonce);
26
+ await this.peer.base.append({ type: 'feature', key: this.key + '_' + key, value : {
27
+ dispatch : {
28
+ type : this.key + '_feature',
29
+ key : key,
30
+ hash : hash,
31
+ value : value,
32
+ nonce : nonce,
33
+ address : this.peer.wallet.publicKey
34
+ }
35
+ }});
36
+ } else {
37
+ console.log('Peer running features not writable.');
38
+ }
35
39
  }
36
40
 
37
41
  async start(options = {}) {
package/src/functions.js CHANGED
@@ -334,30 +334,36 @@ export async function removeWriter(input, peer){
334
334
  }
335
335
  }
336
336
 
337
+ export async function joinValidator(input, peer){
338
+ const splitted = peer.protocol_instance.parseArgs(input)
339
+ const address = ''+splitted.address;
340
+ const validator = await peer.msb.base.view.get(address);
341
+ if(validator === null || false === validator.value.isWriter || true === validator.value.isIndexer){
342
+ throw new Error('Invalid validator address.');
343
+ }
344
+ await peer.tryConnection(address);
345
+ }
346
+
337
347
  export async function tx(input, peer){
338
348
  const splitted = peer.protocol_instance.parseArgs(input);
349
+ let res = false;
339
350
  if(splitted.command === undefined){
340
- throw new Error('Missing option. Please use the --command flag.');
341
- } else if(splitted.sim === undefined && splitted.validator === undefined){
342
- if(peer.validator === null){
343
- throw new Error('No validator available: Please wait for your peer to find an available one before transacting or pass the public key of a known one that is online.');
344
- }
345
- splitted['validator'] = peer.validator;
346
- } else if(splitted.sim !== undefined && splitted.validator !== undefined){
347
- throw new Error('Validator flag not allowed when simulating a transaction.');
351
+ res = new Error('Missing option. Please use the --command flag.');
352
+ } else if(splitted.sim === undefined && peer.validator === null){
353
+ res = new Error('No validator available: Please wait for your peer to find an available one or use joinValidator to connect to a specific one.');
348
354
  }
349
- let res = false;
355
+ let sim = false;
350
356
  try{
351
357
  if(splitted.sim !== undefined && parseInt(splitted.sim) === 1){
352
- while(true === peer.protocol_instance.sim) await peer.sleep(3);
353
- peer.protocol_instance.sim = true;
358
+ sim = true;
354
359
  }
355
- res = await peer.protocol_instance.tx(splitted);
360
+ res = await peer.protocol_instance.tx(splitted, sim);
356
361
  } catch(e){ console.log(e) }
357
- const err = peer.protocol_instance.getError(res);
358
- if(null !== err){
359
- console.log(err.message);
362
+ if(res !== false){
363
+ const err = peer.protocol_instance.getError(res);
364
+ if(null !== err){
365
+ console.log(err.message);
366
+ }
360
367
  }
361
- peer.protocol_instance.sim = false;
362
368
  return res;
363
369
  }
package/src/index.js CHANGED
@@ -11,11 +11,13 @@ 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,
17
19
  muteStatus, setWhitelistStatus, updateAdmin, tx, safeClone, jsonParse,
18
- pinMessage
20
+ pinMessage, joinValidator, removeWriter
19
21
  } from "./functions.js";
20
22
  import Check from "./check.js";
21
23
  export {default as Protocol} from "./protocol.js";
@@ -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;
@@ -124,7 +127,8 @@ export class Peer extends ReadyResource {
124
127
  let _err = null;
125
128
  if(null !== err) {
126
129
  if(err.constructor.name === 'UnknownContractOperationType') continue;
127
- _err = ''+err.message;
130
+ const _err_msg = parseInt(err.message);
131
+ _err = isNaN(_err_msg) ? ''+err.message : _err_msg;
128
132
  }
129
133
  let len = await batch.get('txl');
130
134
  if(null === len) {
@@ -132,12 +136,13 @@ export class Peer extends ReadyResource {
132
136
  } else {
133
137
  len = len.value;
134
138
  }
135
- const cloned = safeClone(op.value.dispatch);
136
- cloned['err'] = _err;
137
- cloned['tx'] = post_tx.value.tx;
138
- cloned['ipk'] = post_tx.value.ipk;
139
- cloned['wp'] = post_tx.value.wp;
140
- await batch.put('txi/'+len, cloned);
139
+ const dta = {};
140
+ dta['val'] = safeClone(op.value.dispatch);
141
+ dta['err'] = _err;
142
+ dta['tx'] = post_tx.value.tx;
143
+ dta['ipk'] = post_tx.value.ipk;
144
+ dta['wp'] = post_tx.value.wp;
145
+ await batch.put('txi/'+len, dta);
141
146
  await batch.put('txl', len + 1);
142
147
  await batch.put('tx/'+post_tx.value.tx, len);
143
148
  let ulen = await batch.get('utxl/'+post_tx.value.ipk);
@@ -229,7 +234,7 @@ export class Peer extends ReadyResource {
229
234
  const verified = _this.wallet.verify(op.hash, str_msg + op.nonce, admin.value);
230
235
  if(true === verified){
231
236
  const writerKey = b4a.from(op.key, 'hex');
232
- base.addWriter(writerKey, { isIndexer : true });
237
+ await base.addWriter(writerKey, { isIndexer : true });
233
238
  await batch.put('sh/'+op.hash, '');
234
239
  console.log(`Indexer added: ${op.key}`);
235
240
  }
@@ -245,9 +250,9 @@ export class Peer extends ReadyResource {
245
250
  const verified = _this.wallet.verify(op.hash, str_msg + op.nonce, admin.value);
246
251
  if(true === verified){
247
252
  const writerKey = b4a.from(op.key, 'hex');
248
- base.addWriter(writerKey, { isIndexer : false });
253
+ await base.addWriter(writerKey, { isIndexer : false });
249
254
  await batch.put('sh/'+op.hash, '');
250
- console.log(`Writer added: ${op.key}`);
255
+ console.log(`Writer added: ${writerKey}`);
251
256
  }
252
257
  }
253
258
  } else if (op.type === 'removeWriter') {
@@ -261,7 +266,7 @@ export class Peer extends ReadyResource {
261
266
  const verified = _this.wallet.verify(op.hash, str_msg + op.nonce, admin.value);
262
267
  if(true === verified){
263
268
  const writerKey = b4a.from(op.key, 'hex');
264
- base.removeWriter(writerKey);
269
+ await base.removeWriter(writerKey);
265
270
  await batch.put('sh/'+op.hash, '');
266
271
  console.log(`Writer removed: ${op.key}`);
267
272
  }
@@ -302,7 +307,7 @@ export class Peer extends ReadyResource {
302
307
  const banned = await batch.get('bnd/'+op.key);
303
308
  if(null === banned && null !== auto_add_writers && auto_add_writers.value === 'on'){
304
309
  const writerKey = b4a.from(op.key, 'hex');
305
- base.addWriter(writerKey, { isIndexer : false });
310
+ await base.addWriter(writerKey, { isIndexer : false });
306
311
  }
307
312
  console.log(`Writer auto added: ${op.key}`);
308
313
  } else if (op.type === 'addAdmin') {
@@ -510,6 +515,36 @@ export class Peer extends ReadyResource {
510
515
  try{ await this.validator_stream.send(b4a.from(jsonStringify(_msg))); } catch(e){ }
511
516
  }
512
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
+
513
548
  async isValidatorAvailable(address){
514
549
  if(null === this.msb.getSwarm()) return null;
515
550
  let writer_key = null;
@@ -527,7 +562,9 @@ export class Peer extends ReadyResource {
527
562
  existing_stream.on('message', (msg) => {
528
563
  try{
529
564
  const response = jsonParse(b4a.toString(msg, 'utf-8'));
530
- 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')){
531
568
  writer_key = response.key;
532
569
  }
533
570
  }catch(e){}
@@ -551,7 +588,9 @@ export class Peer extends ReadyResource {
551
588
  stream.on('message', (msg) => {
552
589
  try{
553
590
  const response = jsonParse(b4a.toString(msg, 'utf-8'));
554
- 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')){
555
594
  writer_key = response.key;
556
595
  }
557
596
  }catch(e){}
@@ -621,50 +660,23 @@ export class Peer extends ReadyResource {
621
660
  } else {
622
661
  length = length.value;
623
662
  }
663
+ if(this.custom_validators.length !== 0){
664
+ length = this.custom_validators.length;
665
+ }
624
666
  async function findSome(){
625
667
  if(_this.validator_stream !== null) return;
626
668
  const rnd_index = Math.floor(Math.random() * length);
627
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
+ }
628
674
  if(_this.validator_stream !== null) return;
629
675
  if (null !== validator) {
630
676
  validator = await _this.msb.base.view.get(validator.value);
631
677
  if(_this.validator_stream !== null) return;
632
678
  if(null !== validator && false !== validator.value.isWriter && false === validator.value.isIndexer) {
633
- const result = await _this.isValidatorAvailable(validator.value.pub);
634
- if(_this.validator_stream !== null) return;
635
- if (null !== result) {
636
- await _this.sleep(100);
637
- if(_this.validator_stream !== null) return;
638
- let existing_stream = undefined;
639
- if(_this.msb.getSwarm().peers.has(validator.value.pub)){
640
- const peerInfo = _this.msb.getSwarm().peers.get(validator.value.pub)
641
- existing_stream = _this.msb.getSwarm()._allConnections.get(peerInfo.publicKey)
642
- }
643
- if(existing_stream !== undefined){
644
- _this.validator_stream = existing_stream;
645
- _this.validator = validator.value.pub;
646
- _this.validator_stream.on('close', () => {
647
- _this.validator_stream = null;
648
- _this.validator = null;
649
- console.log('Validator stream closed', validator.value.pub);
650
- });
651
- console.log('Validator stream established', validator.value.pub);
652
- } else {
653
- _this.validator_stream = _this.msb.getSwarm().dht.connect(b4a.from(validator.value.pub, 'hex'));
654
- _this.validator = validator.value.pub;
655
- _this.validator_stream.on('open', () => {
656
- console.log('Validator stream established', validator.value.pub);
657
- });
658
- _this.validator_stream.on('close', () => {
659
- _this.validator_stream = null;
660
- _this.validator = null;
661
- });
662
- _this.validator_stream.on('error', (error) => {
663
- _this.validator_stream = null;
664
- _this.validator = null;
665
- });
666
- }
667
- }
679
+ await _this.tryConnection(validator.value.pub);
668
680
  }
669
681
  }
670
682
  }
@@ -694,10 +706,10 @@ export class Peer extends ReadyResource {
694
706
  const msb_tx = await view_session.get(tx);
695
707
  await view_session.close();
696
708
  if(null !== msb_tx){
697
- msb_tx['dispatch'] = this.protocol_instance.prepared_transactions_content[tx].dispatch;
698
709
  msb_tx['msbsl'] = msbsl;
710
+ msb_tx['dispatch'] = this.protocol_instance.prepared_transactions_content[tx].dispatch;
699
711
  msb_tx['ipk'] = this.protocol_instance.prepared_transactions_content[tx].ipk;
700
- msb_tx['wp'] = this.validator;
712
+ msb_tx['wp'] = this.protocol_instance.prepared_transactions_content[tx].validator;
701
713
  delete this.tx_pool[tx];
702
714
  delete this.protocol_instance.prepared_transactions_content[tx];
703
715
  await this.base.append({ type: 'tx', key: tx, value: msb_tx });
@@ -754,16 +766,51 @@ export class Peer extends ReadyResource {
754
766
  poll: 5000
755
767
  });
756
768
  console.log('');
757
- console.log('############################################################################');
758
- console.log('# Hypermall Address: ', this.wallet.publicKey, '#');
769
+ console.log('######################################################################################');
770
+ console.log('# Peer Address: ', this.wallet.publicKey, '#');
759
771
  this.writerLocalKey = b4a.toString(this.base.local.key, 'hex');
760
- console.log('# Hypermall Writer: ', this.writerLocalKey, '#');
761
- console.log('############################################################################');
772
+ console.log('# Peer Writer: ', this.writerLocalKey, '#');
773
+ console.log('######################################################################################');
774
+ console.log('');
762
775
  console.log(`isIndexer: ${this.base.isIndexer}`);
763
776
  console.log(`isWriter: ${this.base.writable}`);
764
777
  console.log('');
765
778
 
766
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
+
767
814
  const remotePublicKey = b4a.toString(connection.remotePublicKey, 'hex');
768
815
 
769
816
  this.connectedPeers.add(remotePublicKey);
@@ -772,6 +819,11 @@ export class Peer extends ReadyResource {
772
819
  this.connectedNodes++;
773
820
 
774
821
  connection.on('close', () => {
822
+ if(this.validator_stream === connection){
823
+ this.validator_stream = null;
824
+ this.validator = null;
825
+ }
826
+ message_channel.close()
775
827
  this.connectedNodes--;
776
828
  this.connectedPeers.delete(remotePublicKey);
777
829
  });
@@ -855,6 +907,10 @@ export class Peer extends ReadyResource {
855
907
  const lengthdagSystem = this.base.system.core.length;
856
908
  console.log('wallet.address:', this.wallet !== null ? this.wallet.publicKey : 'unset');
857
909
  console.log('hypermall.writerKey:', this.writerLocalKey);
910
+ const admin = await this.base.view.get('admin')
911
+ console.log(`admin: ${admin !== null ? admin.value : 'unset'}`);
912
+ console.log(`isIndexer: ${this.base.isIndexer}`);
913
+ console.log(`isWriter: ${this.base.writable}`);
858
914
  console.log('swarm.connections.size:', this.swarm.connections.size);
859
915
  console.log('base.view.core.signedLength:', this.base.view.core.signedLength);
860
916
  console.log("base.signedLength", this.base.signedLength);
@@ -871,16 +927,12 @@ export class Peer extends ReadyResource {
871
927
  }
872
928
  }
873
929
 
874
- async interactiveMode() {
875
- if(this.readline_instance === null || (global.Pear !== undefined && global.Pear.config.options.type === 'desktop')) return;
876
-
877
- const rl = this.readline_instance;
878
-
930
+ printHelp(){
879
931
  console.log('Node started. Available commands:');
880
932
  console.log(' ');
881
933
  console.log('- Setup Commands:');
882
934
  console.log('- /add_admin | Works only once and only on bootstrap node! Enter a wallet address to assign admin rights: \'/add_admin --address "<address>"\'.');
883
- console.log('- /update_admin | Existing admins may transfer admin ownership. Enter "null" as address to waive admin rights for this peer entirely: \'/add_admin --address "<address>"\'.');
935
+ console.log('- /update_admin | Existing admins may transfer admin ownership. Enter "null" as address to waive admin rights for this peer entirely: \'/update_admin --address "<address>"\'.');
884
936
  console.log('- /add_indexer | Only admin. Enter a peer writer key to get included as indexer for this network: \'/add_indexer --key "<key>"\'.');
885
937
  console.log('- /add_writer | Only admin. Enter a peer writer key to get included as writer for this network: \'/add_writer --key "<key>"\'.');
886
938
  console.log('- /remove_writer | Only admin. Enter a peer writer key to get removed as writer or indexer for this network: \'/remove_writer --key "<key>"\'.');
@@ -898,18 +950,31 @@ export class Peer extends ReadyResource {
898
950
  console.log('- /set_whitelist_status | Only admin. Add/remove users to/from the chat whitelist: \'/set_whitelist_status --user "<address>" --status 1\'.');
899
951
  console.log(' ');
900
952
  console.log('- System Commands:');
901
- console.log('- /tx | Perform a contract transaction. The command flag contains contract commands in json format: \'/tx --command "<string, content depends on the protocol>"\'. To try broadcasting to a specific validator, use the validator flag and pass the validator public key: --validator "<validator public key>". To simulate a tx, you must leave out the \'--validator\' flag and use \'--sim 1\' instead.');
953
+ console.log('- /tx | Perform a contract transaction. The command flag contains contract commands (format is protocol dependent): \'/tx --command "<string>"\'. To simulate a tx, additionally use \'--sim 1\'.');
954
+ console.log('- /join_validator | Try to connect to a specific validator with its MSB address: \'/join_validator --address "<address>"\'.');
902
955
  console.log('- /stats | check system properties such as writer key, DAG, etc.');
903
956
  console.log('- /get_keys | prints your public and private keys. Be careful and never share your private key!');
904
957
  console.log('- /exit | Exit the program');
958
+ console.log('- /help | This help text');
905
959
 
906
960
  this.protocol_instance.printOptions();
961
+ }
962
+
963
+ async interactiveMode() {
964
+ if(this.readline_instance === null || (global.Pear !== undefined && global.Pear.config.options.type === 'desktop')) return;
965
+
966
+ const rl = this.readline_instance;
967
+
968
+ this.printHelp();
907
969
 
908
970
  rl.on('line', async (input) => {
909
971
  switch (input) {
910
972
  case '/stats':
911
973
  await this.verifyDag();
912
974
  break;
975
+ case '/help':
976
+ await this.printHelp();
977
+ break;
913
978
  case '/exit':
914
979
  console.log('Exiting...');
915
980
  rl.close();
@@ -925,6 +990,8 @@ export class Peer extends ReadyResource {
925
990
  await tx(input, this);
926
991
  } else if (input.startsWith('/add_indexer') || input.startsWith('/add_writer')) {
927
992
  await addWriter(input, this);
993
+ } else if (input.startsWith('/remove_writer')) {
994
+ await removeWriter(input, this);
928
995
  } else if (input.startsWith('/add_admin')) {
929
996
  await addAdmin(input, this);
930
997
  } else if (input.startsWith('/update_admin')) {
@@ -949,6 +1016,8 @@ export class Peer extends ReadyResource {
949
1016
  await enableWhitelist(input, this);
950
1017
  } else if (input.startsWith('/set_whitelist_status')) {
951
1018
  await setWhitelistStatus(input, this);
1019
+ } else if (input.startsWith('/join_validator')) {
1020
+ await joinValidator(input, this);
952
1021
  } else {
953
1022
  await this.protocol_instance.customCommand(input);
954
1023
  }
package/src/protocol.js CHANGED
@@ -17,8 +17,6 @@ class Protocol{
17
17
  this.safeClone = safeClone;
18
18
  this.prepared_transactions_content = {};
19
19
  this.features = {};
20
- this.sim = false;
21
- this.surrogate_tx = null;
22
20
  }
23
21
 
24
22
  featMaxBytes(){
@@ -102,31 +100,27 @@ class Protocol{
102
100
  return await this.peer.createHash('sha256', await this.peer.createHash('sha256', tx));
103
101
  }
104
102
 
105
- async simulateTransaction(obj){
103
+ async simulateTransaction(validator_pub_key, obj, surrogate = null){
106
104
  const storage = new SimStorage(this.peer);
107
- const null_hex = '0000000000000000000000000000000000000000000000000000000000000000';
108
105
  let nonce = this.generateNonce();
109
106
  const content_hash = await this.peer.createHash('sha256', this.safeJsonStringify(obj));
110
- let tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, null_hex,
111
- this.peer.writerLocalKey, this.surrogate_tx !== null ? this.surrogate_tx.address : this.peer.wallet.publicKey, content_hash, nonce);
107
+ let tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, validator_pub_key,
108
+ this.peer.writerLocalKey, surrogate !== null ? surrogate.address : this.peer.wallet.publicKey, content_hash, nonce);
112
109
  const op = {
113
110
  type : 'tx',
114
111
  key : tx,
115
112
  value : {
116
113
  dispatch : obj,
117
114
  value : {
118
- ipk : this.surrogate_tx !== null ? this.surrogate_tx.address : this.peer.wallet.publicKey,
119
- wp : null_hex
115
+ ipk : surrogate !== null ? surrogate.address : this.peer.wallet.publicKey,
116
+ wp : validator_pub_key
120
117
  }
121
118
  }
122
119
  }
123
120
  return await this.peer.contract_instance.execute(op, storage);
124
121
  }
125
122
 
126
- async broadcastTransaction(validator_pub_key, obj){
127
- if(true === this.sim) {
128
- return await this.simulateTransaction(obj);
129
- }
123
+ async broadcastTransaction(validator_pub_key, obj, sim = false, surrogate = null){
130
124
  if(this.peer.validator_stream !== null &&
131
125
  this.peer.wallet.publicKey !== null &&
132
126
  this.peer.wallet.secretKey !== null &&
@@ -134,15 +128,18 @@ class Protocol{
134
128
  obj.type !== undefined &&
135
129
  obj.value !== undefined)
136
130
  {
131
+ if(true === sim) {
132
+ return await this.simulateTransaction(validator_pub_key, obj, surrogate);
133
+ }
137
134
 
138
135
  let tx, signature, nonce, publicKey;
139
136
  const content_hash = await this.peer.createHash('sha256', this.safeJsonStringify(obj));
140
137
 
141
- if(this.surrogate_tx !== null) {
142
- nonce = this.surrogate_tx.nonce;
143
- tx = this.surrogate_tx.tx;
144
- signature = this.surrogate_tx.signature;
145
- publicKey = this.surrogate_tx.address;
138
+ if(surrogate !== null) {
139
+ nonce = surrogate.nonce;
140
+ tx = surrogate.tx;
141
+ signature = surrogate.signature;
142
+ publicKey = surrogate.address;
146
143
  } else {
147
144
  nonce = this.generateNonce();
148
145
  tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, validator_pub_key,
@@ -151,7 +148,7 @@ class Protocol{
151
148
  publicKey = this.peer.wallet.publicKey;
152
149
  }
153
150
 
154
- this.peer.emit('tx', {
151
+ const _tx = {
155
152
  op: 'pre-tx',
156
153
  tx: tx,
157
154
  is: signature,
@@ -162,12 +159,14 @@ class Protocol{
162
159
  in : nonce,
163
160
  bs : this.peer.bootstrap,
164
161
  mbs : this.peer.msb.bootstrap
165
- });
166
- this.prepared_transactions_content[tx] = { dispatch : obj, ipk : publicKey };
162
+ };
163
+
164
+ this.peer.emit('tx', _tx);
165
+ this.prepared_transactions_content[tx] = { dispatch : obj, ipk : publicKey, validator : validator_pub_key };
166
+ return _tx;
167
167
  } else {
168
168
  throw Error('broadcastTransaction(writer, obj): Cannot prepare transaction. Please make sure inputs and local writer are set.');
169
169
  }
170
- return true;
171
170
  }
172
171
 
173
172
  async tokenizeInput(input){
@@ -190,15 +189,16 @@ class Protocol{
190
189
  return null;
191
190
  }
192
191
 
193
- async tx(subject){
192
+ async tx(subject, sim = false, surrogate = null){
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) {
196
- return await this.broadcastTransaction(subject.validator,{
195
+ if(null !== obj && typeof obj.type === 'string' && obj.value !== undefined) {
196
+ return await this.broadcastTransaction(this.peer.validator,{
197
197
  type : obj.type,
198
198
  value : obj.value
199
- });
199
+ }, sim, surrogate);
200
200
  }
201
- return new Error('HyperMallProtocol::tx(): command not found.');
201
+ throw new Error('HyperMallProtocol::tx(): command not found.');
202
202
  }
203
203
 
204
204
  async customCommand(input){ }