trac-msb 0.1.13 → 0.1.15

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.
@@ -1 +1,2 @@
1
- 5caea67fce54e53fd7741d2c4a61d0629b6595f840236abfdf165b64ab9e9f4b
1
+ e914ba0570858924b6cf7bfc1852882cf1fc520f410ea84dc0c0248ba2ca725f
2
+ 01a6fc0108514ef6c8ff7b23e1eb25235724934e7e31bea391903baf15b742b9
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "trac-msb",
3
3
  "main": "msb.mjs",
4
- "version": "0.1.13",
4
+ "version": "0.1.15",
5
5
  "pear": {
6
6
  "name": "trac-msb",
7
7
  "type": "terminal"
@@ -14,13 +14,13 @@
14
14
  "dependencies": {
15
15
  "trac-wallet": "^0.0.42",
16
16
  "hyperbee": "2.24.2",
17
- "hypercore": "11.5.0",
18
- "corestore": "7.4.0",
17
+ "hypercore": "11.6.2",
18
+ "corestore": "7.4.1",
19
+ "autobase": "7.6.2",
19
20
  "hypercore-crypto": "3.4.0",
20
21
  "hyperdht": "^6.20.1",
21
22
  "hyperswarm": "4.11.5",
22
23
  "b4a": "1.6.7",
23
- "autobase": "7.4.0",
24
24
  "ready-resource": "^1.0.0",
25
25
  "bare-readline": "^1.0.7",
26
26
  "readline": "npm:bare-node-readline",
package/src/index.js CHANGED
@@ -24,7 +24,6 @@ import {
24
24
  } from './utils/constants.js';
25
25
  import Network from './network.js';
26
26
  import Check from './utils/check.js';
27
- import DHT from 'hyperdht'
28
27
 
29
28
  export class MainSettlementBus extends ReadyResource {
30
29
  // Internal flags
@@ -87,13 +86,13 @@ export class MainSettlementBus extends ReadyResource {
87
86
  this.#opts = options;
88
87
  this.#readline_instance = null;
89
88
  this.enable_interactive_mode = options.enable_interactive_mode !== false;
90
- if(this.enable_interactive_mode !== false){
91
- try{
89
+ if (this.enable_interactive_mode !== false) {
90
+ try {
92
91
  this.#readline_instance = readline.createInterface({
93
92
  input: new tty.ReadStream(0),
94
93
  output: new tty.WriteStream(1)
95
94
  });
96
- }catch(e){ }
95
+ } catch (e) { }
97
96
  }
98
97
  }
99
98
 
@@ -159,6 +158,7 @@ export class MainSettlementBus extends ReadyResource {
159
158
  [OperationType.REMOVE_WRITER]: this.#handleApplyRemoveWriterOperation.bind(this),
160
159
  [OperationType.ADD_INDEXER]: this.#handleApplyAddIndexerOperation.bind(this),
161
160
  [OperationType.REMOVE_INDEXER]: this.#handleApplyRemoveIndexerOperation.bind(this),
161
+ [OperationType.BAN_VALIDATOR]: this.#handleApplyBanValidatorOperation.bind(this),
162
162
  };
163
163
  return handlers[type] || null;
164
164
  }
@@ -170,8 +170,8 @@ export class MainSettlementBus extends ReadyResource {
170
170
  null === await batch.get(op.key) &&
171
171
  this.check.sanitizePostTx(op) &&
172
172
  op.key === postTx.tx &&
173
- this.#wallet.verify(b4a.from(postTx.is, 'hex'), b4a.from(postTx.tx + postTx.in), b4a.from(postTx.ipk, 'hex')) && // sender verification
174
- this.#wallet.verify(b4a.from(postTx.ws, 'hex'), b4a.from(postTx.tx + postTx.wn), b4a.from(postTx.wp, 'hex')) && // writer verification
173
+ this.#wallet.verify(b4a.from(postTx.is, 'hex'), b4a.from(postTx.tx + postTx.in), b4a.from(postTx.ipk, 'hex')) &&
174
+ this.#wallet.verify(b4a.from(postTx.ws, 'hex'), b4a.from(postTx.tx + postTx.wn), b4a.from(postTx.wp, 'hex')) &&
175
175
  postTx.tx === await this.generateTx(postTx.bs, this.bootstrap, postTx.wp, postTx.i, postTx.ipk, postTx.ch, postTx.in) &&
176
176
  b4a.byteLength(JSON.stringify(postTx)) <= 4096
177
177
  ) {
@@ -240,6 +240,11 @@ export class MainSettlementBus extends ReadyResource {
240
240
  await batch.put(whitelistKey, true);
241
241
  }
242
242
 
243
+ async #deleteWhitelistEntry(batch, pubKey) {
244
+ const whitelistKey = WHITELIST_PREFIX + pubKey;
245
+ await batch.del(whitelistKey);
246
+ }
247
+
243
248
  async #handleApplyAddWriterOperation(op, view, base, node, batch) {
244
249
  const adminEntry = await batch.get(EntryType.ADMIN);
245
250
  if (null === adminEntry || !this.check.sanitizeAdminAndWritersOperations(op) || !this.#isAdmin(adminEntry.value, node)) return;
@@ -258,18 +263,18 @@ export class MainSettlementBus extends ReadyResource {
258
263
  if (nodeEntry === null || !nodeEntry.value.isWriter) {
259
264
  await base.addWriter(b4a.from(op.value.wk, 'hex'), { isIndexer: false })
260
265
  await batch.put(op.key, {
261
- pub : op.value.pub,
266
+ pub: op.value.pub,
262
267
  wk: op.value.wk,
263
268
  isWriter: true,
264
269
  isIndexer: false
265
270
  });
266
271
  let length = await batch.get('wrl');
267
- if(null === length){
272
+ if (null === length) {
268
273
  length = 0;
269
274
  } else {
270
275
  length = length.value;
271
276
  }
272
- await batch.put('wri/'+length, op.value.pub);
277
+ await batch.put('wri/' + length, op.value.pub);
273
278
  await batch.put('wrl', length + 1);
274
279
  console.log(`Writer added: ${op.key}:${op.value.wk}`);
275
280
  }
@@ -304,7 +309,8 @@ export class MainSettlementBus extends ReadyResource {
304
309
  }
305
310
 
306
311
  await batch.put(op.key, nodeEntry);
307
- console.log(`Writer removed: ${op.key}:${op.value.wk}`);
312
+ console.log(`Writer removed: ${op.key}${op.value.wk ? `:${op.value.wk}` : ''}`);
313
+
308
314
  }
309
315
  }
310
316
 
@@ -349,8 +355,7 @@ export class MainSettlementBus extends ReadyResource {
349
355
  if (!this.check.sanitizeIndexerOrWhitelistOperations(op)) return;
350
356
  const adminEntry = await batch.get(EntryType.ADMIN);
351
357
  let indexersEntry = await batch.get(EntryType.INDEXERS);
352
- if (null === adminEntry || !this.#isAdmin(adminEntry.value, node) || null === indexersEntry || !Array.from(indexersEntry.value).includes(op.key) || Array.from(indexersEntry.value).length <= 1) return;
353
- // TODO: is the below an admin signature? -yes
358
+ if (null === adminEntry || !this.#isAdmin(adminEntry.value, node) || null === indexersEntry || !Array.from(indexersEntry.value).includes(op.key) || Array.from(indexersEntry.value).length <= 1) return;
354
359
  const isMessageVerifed = await this.#verifyMessage(op.value.sig, adminEntry.value.tracPublicKey, MsgUtils.createMessage(op.key, op.value.nonce, op.type))
355
360
  if (isMessageVerifed) {
356
361
  let nodeEntry = await batch.get(op.key);
@@ -374,13 +379,29 @@ export class MainSettlementBus extends ReadyResource {
374
379
  }
375
380
  }
376
381
 
382
+ async #handleApplyBanValidatorOperation(op, view, base, node, batch) {
383
+ const adminEntry = await batch.get(EntryType.ADMIN);
384
+ if (null === adminEntry || !this.#isAdmin(adminEntry.value, node)) return;
385
+ if (!this.check.sanitizeIndexerOrWhitelistOperations(op)) return;
386
+ const isWhitelisted = await this.#isWhitelisted2(op.key, batch);
387
+ if (!isWhitelisted) return;
388
+
389
+ const nodeEntry = await batch.get(op.key)
390
+ if (null === nodeEntry || nodeEntry.value.isIndexer === true) return; // even if node is not writable atm it should be possible to ban it.
391
+ const isMessageVerifed = await this.#verifyMessage(op.value.sig, adminEntry.value.tracPublicKey, MsgUtils.createMessage(op.key, op.value.nonce, op.type))
392
+ if (!isMessageVerifed) return;
393
+ await this.#deleteWhitelistEntry(batch, op.key);
394
+ await this.#removeWriter(op, batch, base);
395
+
396
+ }
397
+
377
398
  async _open() {
378
399
  await this.#base.ready();
379
400
  if (this.#enable_wallet) {
380
401
  await this.#wallet.initKeyPair(this.KEY_PAIR_PATH, this.#readline_instance);
381
402
  }
382
403
 
383
- if(this.#enable_wallet){
404
+ if (this.#enable_wallet) {
384
405
  console.log('');
385
406
  console.log('#####################################################################################');
386
407
  console.log('# MSB Address: ', this.#wallet.publicKey, '#');
@@ -562,7 +583,7 @@ export class MainSettlementBus extends ReadyResource {
562
583
  });
563
584
 
564
585
  this.#base.on(EventType.UNWRITABLE, async () => {
565
- if(this.#enable_wallet === false) {
586
+ if (this.#enable_wallet === false) {
566
587
  console.log('Current node is unwritable');
567
588
  return;
568
589
  }
@@ -581,7 +602,7 @@ export class MainSettlementBus extends ReadyResource {
581
602
 
582
603
  async #adminEventListener() {
583
604
  this.on(EventType.ADMIN_EVENT, async (parsedRequest) => {
584
- if(this.#enable_wallet === false) return;
605
+ if (this.#enable_wallet === false) return;
585
606
  const isWhitelisted = await this.#isWhitelisted(parsedRequest.key);
586
607
  const isEventMessageVerifed = await MsgUtils.verifyEventMessage(parsedRequest, this.#wallet)
587
608
  if (isWhitelisted && isEventMessageVerifed) {
@@ -592,7 +613,7 @@ export class MainSettlementBus extends ReadyResource {
592
613
 
593
614
  async #writerEventListener() {
594
615
  this.on(EventType.WRITER_EVENT, async (parsedRequest) => {
595
- if(this.#enable_wallet === false) return;
616
+ if (this.#enable_wallet === false) return;
596
617
  const adminEntry = await this.get(EntryType.ADMIN);
597
618
  const isEventMessageVerifed = await MsgUtils.verifyEventMessage(parsedRequest, this.#wallet)
598
619
  if (adminEntry && adminEntry.tracPublicKey === parsedRequest.key && isEventMessageVerifed) {
@@ -649,7 +670,7 @@ export class MainSettlementBus extends ReadyResource {
649
670
  }
650
671
 
651
672
  async #handleWhitelistOperations() {
652
- if(this.#enable_wallet === false) return;
673
+ if (this.#enable_wallet === false) return;
653
674
  const adminEntry = await this.get(EntryType.ADMIN);
654
675
  if (!this.#isAdmin(adminEntry)) return;
655
676
 
@@ -681,7 +702,7 @@ export class MainSettlementBus extends ReadyResource {
681
702
  }
682
703
 
683
704
  async #requestWriterRole(toAdd) {
684
- if(this.#enable_wallet === false) return;
705
+ if (this.#enable_wallet === false) return;
685
706
  const adminEntry = await this.get(EntryType.ADMIN);
686
707
  const nodeEntry = await this.get(this.#wallet.publicKey);
687
708
  const isAlreadyWriter = !!(nodeEntry && nodeEntry.isWriter)
@@ -705,7 +726,7 @@ export class MainSettlementBus extends ReadyResource {
705
726
  }
706
727
 
707
728
  async #updateIndexerRole(tracPublicKey, toAdd) {
708
- if(this.#enable_wallet === false) return;
729
+ if (this.#enable_wallet === false) return;
709
730
  const adminEntry = await this.get(EntryType.ADMIN);
710
731
  if (!this.#isAdmin(adminEntry) && !this.#base.writable) return;
711
732
 
@@ -733,6 +754,18 @@ export class MainSettlementBus extends ReadyResource {
733
754
  }
734
755
  }
735
756
 
757
+ async #banValidator(tracPublicKey) {
758
+ const adminEntry = await this.get(EntryType.ADMIN);
759
+ if (!this.#isAdmin(adminEntry)) return;
760
+ const isWhitelisted = await this.#isWhitelisted(tracPublicKey);
761
+ const nodeEntry = await this.get(tracPublicKey);
762
+ if (!isWhitelisted || null === nodeEntry || nodeEntry.isIndexer === true) return;
763
+
764
+ const assembledBanValidatorMessage = await MsgUtils.assembleBanValidatorMessage(this.#wallet, tracPublicKey);
765
+ this.#base.append(assembledBanValidatorMessage);
766
+
767
+ }
768
+
736
769
  async #handleAddIndexerOperation(tracPublicKey) {
737
770
  this.#updateIndexerRole(tracPublicKey, true);
738
771
  }
@@ -749,7 +782,11 @@ export class MainSettlementBus extends ReadyResource {
749
782
  await this.#requestWriterRole(false);
750
783
  }
751
784
 
752
- printHelp(){
785
+ async #handleBanValidatorOperation(tracPublicKey) {
786
+ await this.#banValidator(tracPublicKey);
787
+ }
788
+
789
+ printHelp() {
753
790
  console.log('Available commands:');
754
791
  console.log('- /add_writer: add yourself as validator to this MSB once whitelisted.');
755
792
  console.log('- /remove_writer: remove yourself from this MSB.');
@@ -757,14 +794,15 @@ export class MainSettlementBus extends ReadyResource {
757
794
  console.log('- /add_whitelist: add all specified whitelist addresses. (admin only)');
758
795
  console.log('- /add_indexer <address>: change a role of the selected writer node to indexer role. (admin only)');
759
796
  console.log('- /remove_indexer <address>: change a role of the selected indexer node to default role. (admin only)');
797
+ console.log('- /ban_writer <address>: demote a whitelisted writer to default role and remove it from the whitelist. (admin only)');
760
798
  console.log('- /get_node_info <address>: get information about a node with the given address.');
761
- console.log('- /dag: check system properties such as writing key, DAG, etc.');
799
+ console.log('- /stats: check system stats such as writing key, DAG, etc.');
762
800
  console.log('- /exit: Exit the program.');
763
801
  console.log('- /help: display this help.');
764
802
  }
765
803
 
766
804
  async interactiveMode() {
767
- if(this.#readline_instance === null) return;
805
+ if (this.#readline_instance === null) return;
768
806
  const rl = this.#readline_instance;
769
807
 
770
808
  this.printHelp();
@@ -807,8 +845,8 @@ export class MainSettlementBus extends ReadyResource {
807
845
  const indexers = await this.get(EntryType.INDEXERS);
808
846
  console.log('Indexers:', indexers);
809
847
  break;
810
- case '/dag':
811
- await verifyDag(this.#base);
848
+ case '/stats':
849
+ await verifyDag(this.#base, this.#swarm, this.#wallet, this.#writingKey);
812
850
  break;
813
851
  default:
814
852
  if (input.startsWith('/get_node_info')) {
@@ -824,6 +862,11 @@ export class MainSettlementBus extends ReadyResource {
824
862
  const tracPublicKey = splitted[1]
825
863
  await this.#handleRemoveIndexerOperation(tracPublicKey);
826
864
  }
865
+ else if (input.startsWith('/ban_writer')) {
866
+ const splitted = input.split(' ');
867
+ const tracPublicKey = splitted[1]
868
+ await this.#handleBanValidatorOperation(tracPublicKey);
869
+ }
827
870
  }
828
871
  rl.prompt();
829
872
  });
package/src/network.js CHANGED
@@ -38,7 +38,7 @@ class Network {
38
38
  };
39
39
  }
40
40
 
41
- swarm = new Hyperswarm({ keyPair, randomPunchInterval: 500, bootstrap : bootstrap, maxPeers: MAX_PEERS, maxParallel: MAX_PARALLEL, maxServerConnections: MAX_SERVER_CONNECTIONS, maxClientConnections : MAX_CLIENT_CONNECTIONS});
41
+ swarm = new Hyperswarm({ keyPair, randomPunchInterval: 5_000, bootstrap : bootstrap, maxPeers: MAX_PEERS, maxParallel: MAX_PARALLEL, maxServerConnections: MAX_SERVER_CONNECTIONS, maxClientConnections : MAX_CLIENT_CONNECTIONS});
42
42
 
43
43
  console.log(`Channel: ${b4a.toString(channel)}`);
44
44
  swarm.on('connection', async (connection) => {
@@ -85,7 +85,6 @@ class Network {
85
85
  //await connection.destroy();
86
86
  if (base.isIndexer || !base.writable) return;
87
87
 
88
- // TODO: decide if a tx rejection should be responded with
89
88
  if (network.tx_pool.length >= 1000) {
90
89
  console.log('pool full');
91
90
  return
@@ -1,5 +1,6 @@
1
1
  import Validator from 'fastest-validator';
2
2
  import { isHexString } from './functions.js';
3
+ import {OperationType} from './constants.js';
3
4
  class Check {
4
5
  #_validator;
5
6
  #_sanitizeAdminAndWritersOperations;
@@ -60,11 +61,12 @@ class Check {
60
61
  this.#_sanitizePreTx = this.#compilePreTxSchema();
61
62
  this.#_sanitizePostTx = this.#compilePostTxSchema();
62
63
  }
63
-
64
+ //TODO: rename this function
64
65
  #compileSanitizationAdminAndWriterOperationsSchema() {
66
+ // TODO: Create constants for int values below
65
67
  const schema = {
66
68
  $$strict: true,
67
- type: { type: 'string', enum: ['addAdmin', 'addWriter', 'removeWriter'], required: true },
69
+ type: { type: 'string', enum: [OperationType.ADD_ADMIN, OperationType.ADD_WRITER, OperationType.REMOVE_WRITER], required: true },
68
70
  key: { type: "is_hex_string", length: 64, required: true },
69
71
  value: {
70
72
  $$strict: true,
@@ -82,11 +84,12 @@ class Check {
82
84
  sanitizeAdminAndWritersOperations(op) {
83
85
  return this.#_sanitizeAdminAndWritersOperations(op) === true;
84
86
  }
85
-
87
+ //TODO: rename this function
86
88
  #compileIndexerOrWhitelistOperationSchema() {
89
+ // TODO: Create constants for int values below
87
90
  const schema = {
88
91
  $$strict: true,
89
- type: { type: 'string', enum: ['addIndexer', 'removeIndexer', 'AppendWhitelist'], required: true },
92
+ type: { type: 'string', enum: [OperationType.ADD_INDEXER, OperationType.REMOVE_INDEXER, OperationType.APPEND_WHITELIST, OperationType.BAN_VALIDATOR], required: true },
90
93
  key: { type: "is_hex_string", length: 64, required: true },
91
94
  value: {
92
95
  $$strict: true,
@@ -104,6 +107,7 @@ class Check {
104
107
  }
105
108
 
106
109
  #compilePreTxSchema() {
110
+ // TODO: Create constants for int values below
107
111
  const schema = {
108
112
  $$strict: true,
109
113
  op: { type: 'string', enum: ['pre-tx'], required: true },
@@ -125,6 +129,7 @@ class Check {
125
129
  }
126
130
 
127
131
  #compilePostTxSchema() {
132
+ // TODO: Create constants for int values below
128
133
  const schema = {
129
134
  $$strict: true,
130
135
  type: { type: 'string', enum: ['tx'], required: true },
@@ -11,6 +11,7 @@ export const OperationType = Object.freeze({
11
11
  REMOVE_WRITER: 'removeWriter',
12
12
  ADD_INDEXER: 'addIndexer',
13
13
  REMOVE_INDEXER: 'removeIndexer',
14
+ BAN_VALIDATOR: 'banValidator',
14
15
  TX: 'tx',
15
16
  PRE_TX: 'pre-tx',
16
17
  POST_TX: 'post-tx',
@@ -1,26 +1,31 @@
1
1
  import b4a from 'b4a';
2
2
  import sodium from 'sodium-native';
3
+ import {peer} from "hyperdht/lib/messages.js";
3
4
 
4
5
  export function isHexString(string) {
5
6
  return typeof string === 'string' && /^[0-9a-fA-F]+$/.test(string) && string.length % 2 === 0;
6
7
  }
7
8
 
8
- export async function verifyDag(base) {
9
+ export async function verifyDag(base, swarm, wallet, writerKey) {
9
10
  try {
10
- console.log('--- DAG Monitoring ---');
11
+ console.log('--- Stats ---');
11
12
  const dagView = await base.view.core.treeHash();
12
13
  const lengthdagView = base.view.core.length;
13
14
  const dagSystem = await base.system.core.treeHash();
14
15
  const lengthdagSystem = base.system.core.length;
15
- console.log('this.base.view.core.signedLength:', base.view.core.signedLength);
16
- console.log("this.base.signedLength", base.signedLength);
17
- console.log("this.base.linearizer.indexers.length", base.linearizer.indexers.length);
18
- console.log("this.base.indexedLength", base.indexedLength);
19
- //console.log("this.base.system.core", this.base.system.core);
16
+ console.log('wallet.publicKey:', wallet !== null ? wallet.publicKey : 'unset');
17
+ console.log('msb.writerKey:', writerKey);
18
+ console.log('swarm.connections.size:', swarm.connections.size);
19
+ console.log('base.view.core.signedLength:', base.view.core.signedLength);
20
+ console.log("base.signedLength", base.signedLength);
21
+ console.log("base.indexedLength", base.indexedLength);
22
+ console.log("base.linearizer.indexers.length", base.linearizer.indexers.length);
20
23
  console.log(`base.key: ${base.key.toString('hex')}`);
21
24
  console.log('discoveryKey:', b4a.toString(base.discoveryKey, 'hex'));
22
25
  console.log(`VIEW Dag: ${dagView.toString('hex')} (length: ${lengthdagView})`);
23
26
  console.log(`SYSTEM Dag: ${dagSystem.toString('hex')} (length: ${lengthdagSystem})`);
27
+ const wl = await base.view.get('wrl');
28
+ console.log('Total Registered Writers:', wl !== null ? wl.value : 0);
24
29
 
25
30
  } catch (error) {
26
31
  console.error('Error during DAG monitoring:', error.message);
@@ -43,6 +43,7 @@ class MsgUtils {
43
43
  };
44
44
  break;
45
45
  case OperationType.APPEND_WHITELIST:
46
+ case OperationType.BAN_VALIDATOR:
46
47
  case OperationType.ADD_INDEXER:
47
48
  case OperationType.REMOVE_INDEXER:
48
49
  nonce = Wallet.generateNonce().toString('hex');
@@ -110,6 +111,10 @@ class MsgUtils {
110
111
  return await this.#assembleMessageBase(wallet, writerTracPublicKey, OperationType.REMOVE_INDEXER);
111
112
  }
112
113
 
114
+ static async assembleBanValidatorMessage(wallet, validatorTracPublicKey) {
115
+ return await this.#assembleMessageBase(wallet, validatorTracPublicKey, OperationType.BAN_VALIDATOR);
116
+ }
117
+
113
118
  static async verifyEventMessage(parsedRequest, wallet) {
114
119
  //TODO: Here we can add some sanitization
115
120
  const msg = this.createMessage(parsedRequest.key, parsedRequest.value.wk, parsedRequest.value.nonce, parsedRequest.type);