trac-msb 0.2.26 → 0.3.0-beta.2

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.
Files changed (136) hide show
  1. package/SECURITY.md +2 -2
  2. package/cli/commandHandler.js +317 -20
  3. package/cli/handlers.js +9 -1
  4. package/cli/interactive.js +4 -3
  5. package/cli/tokenSupplyAudit.js +90 -0
  6. package/msb.mjs +1 -0
  7. package/package.json +15 -6
  8. package/rpc/create_server.js +12 -3
  9. package/rpc/handlers.js +121 -0
  10. package/rpc/routes/v1.js +12 -0
  11. package/src/codecs/apply/applyOperationCodec.js +334 -0
  12. package/src/codecs/apply/applyOperations.generated.cjs +6380 -0
  13. package/src/codecs/consensus/v1/consensusV1.generated.cjs +3508 -0
  14. package/src/codecs/consensus/v1/consensusV1OperationCodec.js +161 -0
  15. package/src/codecs/consensus/v1/vdfConfigCodec.js +83 -0
  16. package/src/{utils/protobuf → codecs/network/v1}/networkV1.generated.cjs +664 -393
  17. package/src/codecs/network/v1/networkV1OperationCodec.js +25 -0
  18. package/src/codecs/protobufRuntime.cjs +95 -0
  19. package/src/codecs/state/escrowEntryCodec.js +45 -0
  20. package/src/codecs/state/state.generated.cjs +659 -0
  21. package/src/config/args.js +0 -1
  22. package/src/config/config.js +41 -7
  23. package/src/config/env.js +33 -9
  24. package/src/core/consensus/epochAppenderSelection/EpochAppenderSelectionError.js +55 -0
  25. package/src/core/consensus/epochAppenderSelection/EpochAppenderSelectionOperations.js +118 -0
  26. package/src/core/consensus/epochAppenderSelection/EpochAppenderSelector.js +1042 -0
  27. package/src/core/consensus/epochAppenderSelection/createEpochAppenderSelector.js +23 -0
  28. package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionLedgerReader.js +274 -0
  29. package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionState.js +488 -0
  30. package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionStorage.js +93 -0
  31. package/src/core/consensus/quorum.js +11 -0
  32. package/src/core/consensus/services/EpochCoordinationRound.js +839 -0
  33. package/src/core/consensus/services/EpochCoordinatorOperations.js +211 -0
  34. package/src/core/consensus/services/EpochCoordinatorService.js +335 -0
  35. package/src/core/consensus/services/EpochRoundListeners.js +116 -0
  36. package/src/core/consensus/services/EpochStateMachine.js +141 -0
  37. package/src/core/consensus/services/VDFBare.js +291 -0
  38. package/src/core/consensus/services/VDFNode.js +111 -0
  39. package/src/core/consensus/services/VDFProgressWatchdog.js +79 -0
  40. package/src/core/consensus/services/VDFService.js +87 -0
  41. package/src/core/consensus/services/VDFServiceManager.js +147 -0
  42. package/src/core/consensus/services/createVDFService.js +16 -0
  43. package/src/core/consensus/services/vdf-guardian.js +32 -0
  44. package/src/core/consensus/services/vdf-worker.js +90 -0
  45. package/src/core/network/Network.js +327 -238
  46. package/src/core/network/protocols/consensus/IndexerMessages.js +72 -0
  47. package/src/core/network/protocols/consensus/v1/ConsensusV1ConnectionPolicy.js +57 -0
  48. package/src/core/network/protocols/consensus/v1/ConsensusV1MessageRouter.js +147 -0
  49. package/src/core/network/protocols/consensus/v1/ConsensusV1Protocol.js +72 -0
  50. package/src/core/network/protocols/consensus/v1/IndexerPendingRequestService.js +108 -0
  51. package/src/core/network/protocols/consensus/v1/V1ConsensusProtocolError.js +29 -0
  52. package/src/core/network/protocols/consensus/v1/handlers/V1EpochAppendOperationHandler.js +68 -0
  53. package/src/core/network/protocols/consensus/v1/handlers/V1EpochProofProposalOperationHandler.js +226 -0
  54. package/src/core/network/protocols/consensus/v1/validators/ConsensusValidationSchema.js +192 -0
  55. package/src/core/network/protocols/consensus/v1/validators/V1BaseConsensusOperation.js +428 -0
  56. package/src/core/network/protocols/consensus/v1/validators/V1EpochAppendReply.js +47 -0
  57. package/src/core/network/protocols/consensus/v1/validators/V1EpochAppendRequest.js +17 -0
  58. package/src/core/network/protocols/consensus/v1/validators/V1EpochProofProposalApproval.js +116 -0
  59. package/src/core/network/protocols/consensus/v1/validators/V1EpochProofProposalRequest.js +152 -0
  60. package/src/core/network/protocols/consensus/v1/validators/consensusValidationSchemaHelpers.js +145 -0
  61. package/src/core/network/protocols/shared/ConnectionOperationHandler.js +27 -0
  62. package/src/core/network/protocols/{NetworkMessages.js → validators/ValidatorMessages.js} +24 -14
  63. package/src/core/network/protocols/{ProtocolSession.js → validators/ValidatorProtocolSession.js} +22 -25
  64. package/src/core/network/protocols/{legacy/NetworkMessageRouter.js → validators/legacy/ValidatorLegacyMessageRouter.js} +6 -6
  65. package/src/core/network/protocols/{LegacyProtocol.js → validators/legacy/ValidatorLegacyProtocol.js} +9 -7
  66. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/BaseStateOperationHandler.js +1 -1
  67. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyGetRequestHandler.js +5 -5
  68. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyRoleOperationHandler.js +5 -5
  69. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacySubnetworkOperationHandler.js +5 -5
  70. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyTransferOperationHandler.js +5 -5
  71. package/src/core/network/protocols/{ProtocolInterface.js → validators/shared/ValidatorProtocolInterface.js} +5 -5
  72. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialBootstrapDeploymentValidator.js +1 -1
  73. package/src/core/network/protocols/validators/shared/validators/PartialHtlcValidator.js +325 -0
  74. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialOperationValidator.js +44 -21
  75. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialRoleAccessValidator.js +3 -3
  76. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialTransactionValidator.js +3 -3
  77. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialTransferValidator.js +4 -4
  78. package/src/core/network/protocols/{v1 → validators/v1}/V1ProtocolError.js +1 -1
  79. package/src/core/network/{services/PendingRequestService.js → protocols/validators/v1/ValidatorPendingRequestService.js} +19 -82
  80. package/src/core/network/protocols/{v1/NetworkMessageRouter.js → validators/v1/ValidatorV1MessageRouter.js} +9 -9
  81. package/src/core/network/protocols/{V1Protocol.js → validators/v1/ValidatorV1Protocol.js} +10 -8
  82. package/src/core/network/protocols/{connectionPolicies.js → validators/v1/connectionPolicies.js} +1 -1
  83. package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1BaseOperationHandler.js +6 -23
  84. package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1BroadcastTransactionOperationHandler.js +14 -9
  85. package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1LivenessOperationHandler.js +5 -4
  86. package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionErrorMapper.js +3 -3
  87. package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionOperationStrategies.js +44 -1
  88. package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionPayloadUtils.js +1 -1
  89. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BaseOperation.js +19 -15
  90. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BroadcastTransactionRequest.js +2 -2
  91. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BroadcastTransactionResponse.js +34 -18
  92. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1ValidationSchema.js +3 -3
  93. package/src/core/network/services/IndexerConnectionManager.js +88 -0
  94. package/src/core/network/services/TransactionPoolService.js +11 -18
  95. package/src/core/network/services/TransactionRateLimiterService.js +1 -1
  96. package/src/core/network/services/ValidatorConnectionManager.js +163 -0
  97. package/src/core/network/services/{MessageOrchestrator.js → ValidatorMessageOrchestrator.js} +34 -42
  98. package/src/core/network/services/ValidatorObserverService.js +21 -36
  99. package/src/core/network/shared/BasePendingRequestService.js +67 -0
  100. package/src/core/network/shared/PeerConnectionManager.js +185 -0
  101. package/src/core/state/State.js +1803 -159
  102. package/src/core/state/StateEventQueue.js +69 -0
  103. package/src/core/state/utils/address.js +10 -6
  104. package/src/core/state/utils/epochProof.js +109 -0
  105. package/src/core/state/utils/escrowEntry.js +178 -0
  106. package/src/core/state/validators/StateValidationSchema.js +757 -0
  107. package/src/core/state/validators/stateValidationRules.js +312 -0
  108. package/src/index.js +280 -67
  109. package/src/messages/consensus/v1/ConsensusMessageBuilder.js +368 -0
  110. package/src/messages/consensus/v1/ConsensusMessageDirector.js +142 -0
  111. package/src/messages/consensus/v1/consensusMessageFactory.js +12 -0
  112. package/src/messages/consensus/v1/epochAppendMessage.js +58 -0
  113. package/src/messages/network/v1/NetworkMessageBuilder.js +15 -7
  114. package/src/messages/state/ApplyStateMessageBuilder.js +499 -13
  115. package/src/messages/state/ApplyStateMessageDirector.js +261 -2
  116. package/src/utils/StateMachine.js +125 -0
  117. package/src/utils/applyOperations.js +45 -2
  118. package/src/utils/buffer.js +239 -59
  119. package/src/utils/consensusConfig.js +76 -0
  120. package/src/utils/constants.js +174 -73
  121. package/src/utils/helpers.js +0 -25
  122. package/src/utils/htlcLock.js +122 -0
  123. package/src/utils/logger.js +10 -4
  124. package/src/utils/migrationUtils.js +2 -1
  125. package/src/utils/normalizers.js +113 -0
  126. package/src/utils/scheduler/SchedulableService.js +107 -0
  127. package/src/utils/{Scheduler.js → scheduler/Scheduler.js} +56 -17
  128. package/src/core/network/services/ConnectionManager.js +0 -406
  129. package/src/utils/check.js +0 -525
  130. package/src/utils/protobuf/applyOperations.cjs +0 -1373
  131. package/src/utils/protobuf/operationHelpers.js +0 -81
  132. /package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyResponseHandler.js +0 -0
  133. /package/src/core/network/protocols/{legacy → validators/legacy}/validators/ValidatorResponse.js +0 -0
  134. /package/src/core/network/protocols/{legacy → validators/legacy}/validators/base/BaseResponse.js +0 -0
  135. /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessRequest.js +0 -0
  136. /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessResponse.js +0 -0
package/src/index.js CHANGED
@@ -2,40 +2,55 @@ import ReadyResource from "ready-resource";
2
2
  import Corestore from "corestore";
3
3
  import tracCryptoApi from "trac-crypto-api";
4
4
  import b4a from "b4a";
5
+ import _ from "lodash";
5
6
  import { sleep, isHexString } from "./utils/helpers.js";
6
7
  import { applyStateMessageFactory } from "./messages/state/applyStateMessageFactory.js";
7
- import { isAddressValid } from "./core/state/utils/address.js";
8
+ import { isAddressValid, bufferToAddress } from "./core/state/utils/address.js";
8
9
  import Network from "./core/network/Network.js";
9
- import Check from "./utils/check.js";
10
10
  import State from "./core/state/State.js";
11
11
  import {
12
- EventType,
13
12
  WHITELIST_SLEEP_INTERVAL,
14
13
  BOOTSTRAP_HEXSTRING_LENGTH,
15
- CustomEventType,
16
14
  BALANCE_MIGRATION_SLEEP_INTERVAL,
17
15
  WHITELIST_MIGRATION_DIR,
18
- OperationType
16
+ OperationType,
17
+ EntryType,
18
+ HTLC_LOCK_ID_BYTE_LENGTH,
19
+ ConsensusVersion,
20
+ MAX_VDF_DIFFICULTY,
21
+ VDF_PROOF_BYTE_LENGTHS
19
22
  } from "./utils/constants.js";
20
23
  import { decimalStringToBigInt, bigIntTo16ByteBuffer, bufferToBigInt, bigIntToDecimalString } from "./utils/amountSerialization.js"
21
24
  import {
22
25
  normalizeDecodedPayloadForJson,
23
26
  normalizeTransactionOperation,
24
- normalizeTransferOperation
27
+ normalizeTransferOperation,
28
+ normalizeMessageByOperationType
25
29
  } from "./utils/normalizers.js";
30
+ import { operationToPayload } from "./utils/applyOperations.js";
31
+ import PartialHtlcValidator from "./core/network/protocols/validators/shared/validators/PartialHtlcValidator.js";
32
+ import { V1ProtocolError } from "./core/network/protocols/validators/v1/V1ProtocolError.js";
33
+ import * as escrowEntryUtils from "./core/state/utils/escrowEntry.js";
26
34
  import fileUtils from './utils/fileUtils.js';
27
35
  import migrationUtils from './utils/migrationUtils.js';
28
- import {safeDecodeApplyOperation, safeEncodeApplyOperation} from "./utils/protobuf/operationHelpers.js";
29
- import PartialTransactionValidator from "./core/network/protocols/shared/validators/PartialTransactionValidator.js";
30
- import PartialTransferValidator from "./core/network/protocols/shared/validators/PartialTransferValidator.js";
36
+ import {
37
+ encodeApplyOperation,
38
+ encodeConsensusConfig,
39
+ safeDecodeApplyOperation,
40
+ safeEncodeApplyOperation,
41
+ decodeEpochRecord
42
+ } from "./codecs/apply/applyOperationCodec.js";
43
+ import { encodeVdfConfig } from "./codecs/consensus/v1/vdfConfigCodec.js";
44
+ import PartialTransactionValidator from "./core/network/protocols/validators/shared/validators/PartialTransactionValidator.js";
45
+ import PartialTransferValidator from "./core/network/protocols/validators/shared/validators/PartialTransferValidator.js";
31
46
  import { BroadcastError, ValidationError } from "./utils/errors.js";
47
+ import { uint8ToBuffer, uint16ToBuffer, uint32ToBuffer } from "./utils/buffer.js";
32
48
 
33
49
  export class MainSettlementBus extends ReadyResource {
34
50
  #store;
35
51
  #wallet;
36
52
  #network;
37
53
  #state;
38
- #isClosing = false;
39
54
  #config
40
55
 
41
56
  /**
@@ -47,7 +62,6 @@ export class MainSettlementBus extends ReadyResource {
47
62
  this.#config = config
48
63
  this.#wallet = wallet;
49
64
  this.#store = new Corestore(this.#config.storesFullPath);
50
- this.check = new Check(this.#config);
51
65
  }
52
66
 
53
67
  get config() {
@@ -71,27 +85,18 @@ export class MainSettlementBus extends ReadyResource {
71
85
 
72
86
  async _open() {
73
87
  this.#state = new State(this.#store, this.#wallet, this.#config);
74
- this.#network = new Network(this.#state, this.#config, this.#wallet?.address ?? null);
88
+ this.#network = new Network(this.#state, this.#store, this.#config, this.#wallet);
75
89
 
76
90
  await this.#state.ready();
77
91
  await this.#network.ready();
78
- await this.#stateEventsListener();
79
92
 
80
93
  if (this.#wallet) {
81
94
  this.#printWalletInfo();
82
95
  }
83
96
 
84
- await this.#network.replicate(
85
- this.#state,
86
- this.#store,
87
- this.#wallet,
88
- );
89
-
90
97
  const adminEntry = await this.#state.getAdminEntry();
91
98
  await this.#setUpRoleAutomatically(adminEntry);
92
99
 
93
-
94
-
95
100
  console.log(`isIndexer: ${this.#state.isIndexer()}`);
96
101
  console.log(`isWriter: ${this.#state.isWritable()}`);
97
102
  console.log("MSB Unsigned Length:", this.#state.getUnsignedLength());
@@ -105,7 +110,6 @@ export class MainSettlementBus extends ReadyResource {
105
110
  async _close() {
106
111
  console.log("Closing everything gracefully... This may take a moment.");
107
112
 
108
- this.#isClosing = true;
109
113
  await this.#network.close();
110
114
 
111
115
  await sleep(100);
@@ -129,6 +133,58 @@ export class MainSettlementBus extends ReadyResource {
129
133
  return await this.#network.validatorMessageOrchestrator.send(partialTransactionPayload);
130
134
  }
131
135
 
136
+ async htlcLock(payload) {
137
+ if (payload?.type !== OperationType.HTLC_LOCK) {
138
+ throw new ValidationError('HTLC lock payload is required.');
139
+ }
140
+ return this.broadcastTransaction(payload);
141
+ }
142
+
143
+ async htlcClaim(payload) {
144
+ if (payload?.type !== OperationType.HTLC_CLAIM) {
145
+ throw new ValidationError('HTLC claim payload is required.');
146
+ }
147
+ return this.broadcastTransaction(payload);
148
+ }
149
+
150
+ async htlcRefund(payload) {
151
+ if (payload?.type !== OperationType.HTLC_REFUND) {
152
+ throw new ValidationError('HTLC refund payload is required.');
153
+ }
154
+ return this.broadcastTransaction(payload);
155
+ }
156
+
157
+ async getHtlc(lockId, confirmed = true) {
158
+ if (typeof lockId !== 'string' || lockId.length !== HTLC_LOCK_ID_BYTE_LENGTH * 2 || !isHexString(lockId)) {
159
+ throw new ValidationError('Invalid HTLC lock ID.');
160
+ }
161
+ const normalizedId = lockId.toLowerCase();
162
+ const key = EntryType.HTLC_ESCROW + normalizedId;
163
+ const entry = confirmed ? await this.#state.getSigned(key) : await this.#state.get(key);
164
+ if (!entry) return null;
165
+
166
+ const escrow = escrowEntryUtils.decode(entry, this.#config.addressPrefix);
167
+ if (!escrow || b4a.toString(escrow.lockId, 'hex') !== normalizedId) {
168
+ throw new Error('Stored HTLC escrow is invalid.');
169
+ }
170
+ const address = value => value ? bufferToAddress(value, this.#config.addressPrefix) : null;
171
+ const hex = value => value ? b4a.toString(value, 'hex') : null;
172
+ return {
173
+ lockId: normalizedId,
174
+ status: Object.keys(escrowEntryUtils.Status).find(name => escrowEntryUtils.Status[name] === escrow.status),
175
+ lockerAddress: address(escrow.lockerAddress),
176
+ claimRecipientAddress: address(escrow.claimRecipientAddress),
177
+ refundRecipientAddress: address(escrow.refundRecipientAddress),
178
+ amount: bufferToBigInt(escrow.amount).toString(),
179
+ additionalFeeAmount: bufferToBigInt(escrow.additionalFeeAmount).toString(),
180
+ additionalFeeRecipientAddress: address(escrow.additionalFeeRecipientAddress),
181
+ hashLock: hex(escrow.hashLock),
182
+ refundEpoch: escrow.refundEpoch.readBigUInt64BE(0).toString(),
183
+ metadata: hex(escrow.metadata),
184
+ preimage: hex(escrow.preimage),
185
+ };
186
+ }
187
+
132
188
  async validateTransfer(payload) {
133
189
  const partialTransferValidator = new PartialTransferValidator(this.#state, null, this.#config);
134
190
  return await partialTransferValidator.validate(payload);
@@ -140,6 +196,11 @@ export class MainSettlementBus extends ReadyResource {
140
196
  return await partialTransactionValidator.validate(payload);
141
197
  }
142
198
 
199
+ async validateHtlc(payload) {
200
+ const validator = new PartialHtlcValidator(this.#state, null, this.#config);
201
+ return validator.validate(payload);
202
+ }
203
+
143
204
  async broadcastTransaction(payload) {
144
205
  if (!payload) {
145
206
  throw new ValidationError("Transaction payload is required for broadcasting.");
@@ -161,6 +222,21 @@ export class MainSettlementBus extends ReadyResource {
161
222
  throw new ValidationError("Invalid transaction payload.");
162
223
  }
163
224
  hash = b4a.toString(normalizedPayload.txo.tx, "hex");
225
+ } else if ([OperationType.HTLC_LOCK, OperationType.HTLC_CLAIM, OperationType.HTLC_REFUND].includes(payload.type)) {
226
+ try {
227
+ normalizedPayload = normalizeMessageByOperationType(payload, this.#config);
228
+ } catch (error) {
229
+ throw new ValidationError(error.message || 'Invalid HTLC payload.');
230
+ }
231
+ try {
232
+ await this.validateHtlc(normalizedPayload);
233
+ } catch (error) {
234
+ if (error instanceof V1ProtocolError) throw new ValidationError(error.message);
235
+ throw error;
236
+ }
237
+ hash = b4a.toString(normalizedPayload[operationToPayload(payload.type)].tx, 'hex');
238
+ } else {
239
+ throw new ValidationError('Unsupported transaction operation.');
164
240
  }
165
241
 
166
242
  const success = await this.broadcastPartialTransaction(payload);
@@ -202,37 +278,6 @@ export class MainSettlementBus extends ReadyResource {
202
278
  return nodeEntry?.isWhitelisted && !this.isAdmin(adminEntry);
203
279
  }
204
280
 
205
- async #stateEventsListener() {
206
- this.#state.on(CustomEventType.IS_INDEXER, (publicKey) => {
207
- if (this.#isClosing) return;
208
- this.#network.disconnectValidatorPeer(publicKey, 'peer promoted to indexer');
209
- });
210
-
211
- this.#state.on(CustomEventType.UNWRITABLE, (publicKey) => {
212
- if (this.#isClosing) return;
213
- this.#network.disconnectValidatorPeer(publicKey, 'peer became unwritable');
214
- });
215
-
216
- this.#state.base.on(EventType.IS_INDEXER, () => {
217
- console.log("Current node is an indexer");
218
- });
219
-
220
- this.#state.base.on(EventType.IS_NON_INDEXER, async () => {
221
- // Prevent further actions if closing is in progress
222
- // The reason is that getNodeEntry is async and may cause issues if we will access state after closing
223
- if (this.#isClosing) return;
224
- console.log("Current node is not an indexer anymore");
225
- });
226
-
227
- this.#state.base.on(EventType.WRITABLE, async () => {
228
- console.log("Current node is writable");
229
- });
230
-
231
- this.#state.base.on(EventType.UNWRITABLE, async () => {
232
- console.log("Current node is unwritable");
233
- });
234
- }
235
-
236
281
  async getConfirmedTxInfo(txHash) {
237
282
  const payload = await this.#state.getSigned(txHash);
238
283
  if (!payload) return null
@@ -257,7 +302,7 @@ export class MainSettlementBus extends ReadyResource {
257
302
  return { payload, decoded }
258
303
  }
259
304
 
260
- handleGetFee() {
305
+ handleGetFee() {
261
306
  const fee = this.#state.getFee();
262
307
  return bufferToBigInt(fee);
263
308
  }
@@ -297,7 +342,7 @@ export class MainSettlementBus extends ReadyResource {
297
342
  let dagSystem = await this.#state.base.system.core.treeHash();
298
343
  let lengthdagSystem = this.#state.base.system.core.length;
299
344
  const wl = await this.#state.getWriterLength();
300
-
345
+
301
346
  console.log("---------- node & network stats ----------");
302
347
  console.log("wallet.publicKey:", this.#wallet?.publicKey?.toString("hex") ?? "unset");
303
348
  console.log("wallet.address:", this.#wallet?.address ?? "unset");
@@ -332,6 +377,9 @@ export class MainSettlementBus extends ReadyResource {
332
377
  console.log("- /add_indexer <address>: Change a role of the selected writer node to indexer role. Charges a fee.");
333
378
  console.log("- /remove_indexer <address>: Change a role of the selected indexer node to default role. Charges a fee.");
334
379
  console.log("- /ban_writer <address>: Demote a whitelisted writer to default role and remove it from the whitelist. Charges a fee.");
380
+ console.log("- /init_genesis: Initialize genesis epoch.");
381
+ console.log("- /set_consensus_config: Set new configuration data for a specific consensus schema version from JSON.");
382
+ console.log("- /set_vdf_params: Update VDF difficulty and discriminant bit size.");
335
383
  }
336
384
  console.log("Available commands:");
337
385
  console.log("- /add_writer: Add yourself as a validator to this MSB once whitelisted. Requires a fee + 10x the fee as a stake in $TNK.");
@@ -378,7 +426,16 @@ export class MainSettlementBus extends ReadyResource {
378
426
 
379
427
  async fetchBulkTxPayloads(hashes) {
380
428
  const response = { results: [], missing: [] };
381
- const results = await Promise.all(hashes.map((hash) => this.getConfirmedTxInfo(hash)));
429
+ const results = await Promise.all(hashes.map(async (hash) => {
430
+ const transaction = await this.getConfirmedTxInfo(hash);
431
+ if (transaction) return transaction.decoded;
432
+
433
+ const epochRecord = await this.#state.getEpochProof(hash);
434
+ if (epochRecord === null) return null;
435
+
436
+ // This hash identifies the stored epoch record, not an original transaction.
437
+ return { type: OperationType.SET_EPOCH, seo: decodeEpochRecord(epochRecord) };
438
+ }));
382
439
 
383
440
  results.forEach((result, index) => {
384
441
  const hash = hashes[index];
@@ -387,7 +444,7 @@ export class MainSettlementBus extends ReadyResource {
387
444
  return;
388
445
  }
389
446
 
390
- const decodedResult = normalizeDecodedPayloadForJson(result.decoded, this.#config);
447
+ const decodedResult = normalizeDecodedPayloadForJson(result, this.#config);
391
448
  response.results.push({ hash, payload: decodedResult });
392
449
  });
393
450
 
@@ -746,45 +803,45 @@ export class MainSettlementBus extends ReadyResource {
746
803
  }
747
804
  }
748
805
 
749
- async banValidator(addresstToBan) {
806
+ async banValidator(addressToBan) {
750
807
  if (!this.#config.enableWallet) {
751
808
  throw new Error(
752
- `Can not ban writer with address: ${addresstToBan} - wallet is not enabled.`
809
+ `Can not ban writer with address: ${addressToBan} - wallet is not enabled.`
753
810
  );
754
811
  }
755
812
  const adminEntry = await this.#state.getAdminEntry();
756
813
 
757
814
  if (!adminEntry) {
758
815
  throw new Error(
759
- `Can not ban writer with address: ${addresstToBan} - admin entry has not been initialized.`
816
+ `Can not ban writer with address: ${addressToBan} - admin entry has not been initialized.`
760
817
  );
761
818
  }
762
819
 
763
- if (!isAddressValid(addresstToBan, this.#config.addressPrefix)) {
820
+ if (!isAddressValid(addressToBan, this.#config.addressPrefix)) {
764
821
  throw new Error(
765
- `Can not ban writer with address: ${addresstToBan} - invalid address.`
822
+ `Can not ban writer with address: ${addressToBan} - invalid address.`
766
823
  );
767
824
  }
768
825
 
769
826
  if (!this.isAdmin(adminEntry)) {
770
827
  throw new Error(
771
- `Can not ban writer with address: ${addresstToBan} - You are not an admin.`
828
+ `Can not ban writer with address: ${addressToBan} - You are not an admin.`
772
829
  );
773
830
  }
774
831
 
775
- const isWhitelisted = await this.#state.isAddressWhitelisted(addresstToBan);
776
- const nodeEntry = await this.#state.getNodeEntry(addresstToBan);
832
+ const isWhitelisted = await this.#state.isAddressWhitelisted(addressToBan);
833
+ const nodeEntry = await this.#state.getNodeEntry(addressToBan);
777
834
 
778
835
  if (!isWhitelisted || null === nodeEntry || nodeEntry.isIndexer === true) {
779
836
  throw new Error(
780
- `Can not ban writer with address: ${addresstToBan} - node is not whitelisted or is an indexer.`
837
+ `Can not ban writer with address: ${addressToBan} - node is not whitelisted or is an indexer.`
781
838
  );
782
839
  }
783
840
  const txValidity = await this.#state.getIndexerSequenceState();
784
841
  const assembledBanValidatorMessage = await applyStateMessageFactory(this.#wallet, this.#config)
785
842
  .buildCompleteBanWriterMessage(
786
843
  this.#wallet.address,
787
- addresstToBan,
844
+ addressToBan,
788
845
  txValidity,
789
846
  )
790
847
  const encodedPayload = safeEncodeApplyOperation(assembledBanValidatorMessage)
@@ -1100,6 +1157,162 @@ export class MainSettlementBus extends ReadyResource {
1100
1157
  const encodedPayload = safeEncodeApplyOperation(payload);
1101
1158
  await this.#state.append(encodedPayload);
1102
1159
  }
1160
+
1161
+ async handleEpochGenesisInitialization(consensusConfig) {
1162
+ if (!this.#config.enableWallet) {
1163
+ throw new Error("Can not initialize genesis epoch - wallet is not enabled.");
1164
+ }
1165
+
1166
+ const adminEntry = await this.#state.getAdminEntry();
1167
+
1168
+ if (!adminEntry) {
1169
+ throw new Error(
1170
+ "Can not initialize genesis epoch - admin has not been initialized."
1171
+ );
1172
+ }
1173
+ if (!this.#wallet) {
1174
+ throw new Error("Can not initialize genesis epoch - wallet is not initialized.");
1175
+ }
1176
+ if (!this.isAdmin(adminEntry)) {
1177
+ throw new Error("Can not initialize genesis epoch - you are not the admin.");
1178
+ }
1179
+
1180
+ const existingConsensusConfig = await this.#state.getSignedConsensusConfig();
1181
+ if (existingConsensusConfig) {
1182
+ throw new Error("Can not initialize genesis epoch - consensus config already exists.");
1183
+ }
1184
+
1185
+ const encodedConsensusConfig = this.#validateAndEncodeConsensusConfig(consensusConfig);
1186
+
1187
+ const txValidity = await this.#state.getIndexerSequenceState();
1188
+ const payload = await applyStateMessageFactory(this.#wallet, this.#config)
1189
+ .buildCompleteSetGenesisEpochMessage(
1190
+ this.#wallet.address,
1191
+ txValidity,
1192
+ encodedConsensusConfig,
1193
+ )
1194
+ const encodedPayload = encodeApplyOperation(payload);
1195
+ await this.#state.append(encodedPayload);
1196
+ }
1197
+
1198
+ async handleSetConsensusConfig(consensusConfig) {
1199
+ if (!this.#config.enableWallet) {
1200
+ throw new Error("Can not set consensus config - wallet is not enabled.");
1201
+ }
1202
+
1203
+ const adminEntry = await this.#state.getAdminEntry();
1204
+
1205
+ if (!adminEntry) {
1206
+ throw new Error("Can not set consensus config - admin has not been initialized.");
1207
+ }
1208
+ if (!this.#wallet) {
1209
+ throw new Error("Can not set consensus config - wallet is not initialized.");
1210
+ }
1211
+ if (!this.isAdmin(adminEntry)) {
1212
+ throw new Error("Can not set consensus config - you are not the admin.");
1213
+ }
1214
+
1215
+ const encodedConsensusConfig = this.#validateAndEncodeConsensusConfig(consensusConfig);
1216
+
1217
+ const txValidity = await this.#state.getIndexerSequenceState();
1218
+ const payload = await applyStateMessageFactory(this.#wallet, this.#config)
1219
+ .buildCompleteSetConsensusConfigMessage(
1220
+ this.#wallet.address,
1221
+ txValidity,
1222
+ encodedConsensusConfig
1223
+ );
1224
+ const encodedPayload = encodeApplyOperation(payload);
1225
+ await this.#state.append(encodedPayload);
1226
+ }
1227
+
1228
+ #validateAndEncodeConsensusConfig(consensusConfig) {
1229
+ if (!this.#hasExactKeys(consensusConfig, ["schemaVersion", "configData"])) {
1230
+ throw new Error(
1231
+ "Consensus config must contain only schemaVersion and configData."
1232
+ );
1233
+ }
1234
+
1235
+ const { schemaVersion, configData } = consensusConfig;
1236
+ if (
1237
+ !Number.isInteger(schemaVersion) ||
1238
+ schemaVersion <= 0 ||
1239
+ schemaVersion > 0xFF
1240
+ ) {
1241
+ throw new Error(
1242
+ "Consensus config schemaVersion must be an integer from 1 to 255."
1243
+ );
1244
+ }
1245
+
1246
+ let encodedConfigData;
1247
+ switch (schemaVersion) {
1248
+ case ConsensusVersion.VDF_V1:
1249
+ encodedConfigData = this.#encodeVdfV1ConfigData(configData);
1250
+ break;
1251
+ default:
1252
+ throw new Error(
1253
+ `Unsupported consensus config schema version: ${schemaVersion}.`
1254
+ );
1255
+ }
1256
+
1257
+ return encodeConsensusConfig({
1258
+ sv: uint8ToBuffer(schemaVersion),
1259
+ cd: encodedConfigData
1260
+ });
1261
+ }
1262
+
1263
+ // TODO: REFACTOR - In the future, this function should be extracted into another module.
1264
+ #encodeVdfV1ConfigData(configData) {
1265
+ if (!this.#hasExactKeys(configData, ["difficulty", "discriminantBitSize"])) {
1266
+ throw new Error(
1267
+ "Consensus config schema version 1 configData must contain only difficulty and discriminantBitSize."
1268
+ );
1269
+ }
1270
+
1271
+ if (
1272
+ !Number.isInteger(configData.difficulty) ||
1273
+ configData.difficulty <= 0 ||
1274
+ configData.difficulty > MAX_VDF_DIFFICULTY
1275
+ ) {
1276
+ throw new Error(
1277
+ "VDF difficulty must be a positive unsigned 32-bit integer."
1278
+ );
1279
+ }
1280
+
1281
+ if (
1282
+ !Number.isInteger(configData.discriminantBitSize) ||
1283
+ !Object.hasOwn(VDF_PROOF_BYTE_LENGTHS, configData.discriminantBitSize)
1284
+ ) {
1285
+ throw new Error(
1286
+ "VDF discriminant bit size must be one of: 1024, 2048, 4096."
1287
+ );
1288
+ }
1289
+
1290
+ return encodeVdfConfig({
1291
+ difficulty: uint32ToBuffer(configData.difficulty),
1292
+ discriminantBitSize: uint16ToBuffer(configData.discriminantBitSize)
1293
+ });
1294
+ }
1295
+
1296
+ #hasExactKeys(value, expectedKeys) {
1297
+ return (
1298
+ _.isPlainObject(value) &&
1299
+ Object.keys(value).length === expectedKeys.length &&
1300
+ expectedKeys.every(key => Object.hasOwn(value, key))
1301
+ );
1302
+ }
1303
+
1304
+ async getCurrentEpoch(confirmed = true) {
1305
+ const epoch = confirmed
1306
+ ? await this.#state.getCurrentEpoch()
1307
+ : await this.#state.getCurrentEpochUnsigned();
1308
+ return epoch === null ? null : epoch.toString();
1309
+ }
1310
+
1311
+ async getConsensusConfig(confirmed = true) {
1312
+ return confirmed
1313
+ ? this.#state.getSignedConsensusConfig()
1314
+ : this.#state.getUnsignedConsensusConfig();
1315
+ }
1103
1316
  }
1104
1317
 
1105
1318
  export default MainSettlementBus;