trac-msb 0.2.24 → 0.3.0-beta.1

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 (137) 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 +103 -0
  10. package/rpc/routes/v1.js +8 -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 +46 -7
  23. package/src/config/env.js +33 -6
  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/ConfirmationWatch.js +82 -0
  102. package/src/core/state/State.js +1794 -173
  103. package/src/core/state/StateEventQueue.js +69 -0
  104. package/src/core/state/utils/address.js +10 -6
  105. package/src/core/state/utils/epochProof.js +109 -0
  106. package/src/core/state/utils/escrowEntry.js +178 -0
  107. package/src/core/state/validators/StateValidationSchema.js +757 -0
  108. package/src/core/state/validators/stateValidationRules.js +312 -0
  109. package/src/index.js +255 -65
  110. package/src/messages/consensus/v1/ConsensusMessageBuilder.js +368 -0
  111. package/src/messages/consensus/v1/ConsensusMessageDirector.js +142 -0
  112. package/src/messages/consensus/v1/consensusMessageFactory.js +12 -0
  113. package/src/messages/consensus/v1/epochAppendMessage.js +58 -0
  114. package/src/messages/network/v1/NetworkMessageBuilder.js +15 -7
  115. package/src/messages/state/ApplyStateMessageBuilder.js +499 -13
  116. package/src/messages/state/ApplyStateMessageDirector.js +261 -2
  117. package/src/utils/StateMachine.js +125 -0
  118. package/src/utils/applyOperations.js +45 -2
  119. package/src/utils/buffer.js +239 -59
  120. package/src/utils/consensusConfig.js +76 -0
  121. package/src/utils/constants.js +174 -73
  122. package/src/utils/helpers.js +0 -25
  123. package/src/utils/htlcLock.js +122 -0
  124. package/src/utils/logger.js +10 -4
  125. package/src/utils/migrationUtils.js +2 -1
  126. package/src/utils/normalizers.js +113 -0
  127. package/src/utils/scheduler/SchedulableService.js +107 -0
  128. package/src/utils/{Scheduler.js → scheduler/Scheduler.js} +56 -17
  129. package/src/core/network/services/ConnectionManager.js +0 -406
  130. package/src/utils/check.js +0 -525
  131. package/src/utils/protobuf/applyOperations.cjs +0 -1373
  132. package/src/utils/protobuf/operationHelpers.js +0 -81
  133. /package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyResponseHandler.js +0 -0
  134. /package/src/core/network/protocols/{legacy → validators/legacy}/validators/ValidatorResponse.js +0 -0
  135. /package/src/core/network/protocols/{legacy → validators/legacy}/validators/base/BaseResponse.js +0 -0
  136. /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessRequest.js +0 -0
  137. /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessResponse.js +0 -0
@@ -0,0 +1,152 @@
1
+ import V1BaseConsensusOperation from "./V1BaseConsensusOperation.js";
2
+ import { V1ConsensusProtocolError } from "../V1ConsensusProtocolError.js";
3
+ import { ConsensusResultCode } from "../../../../../../utils/constants.js";
4
+ import { uint16ToBuffer } from "../../../../../../utils/buffer.js";
5
+ import b4a from "b4a";
6
+ import { verifyWesolowski } from '@tracsystems/trac-vdf';
7
+
8
+ class V1EpochProofProposalRequest extends V1BaseConsensusOperation {
9
+
10
+ /**
11
+ * Creates the proof proposal request validator.
12
+ *
13
+ * @param {Config} config Application configuration used by base validation
14
+ * and network identity checks.
15
+ * @param {State} state Ledger state. It must expose `requireCurrentEpoch()`,
16
+ * `requireEpoch(epoch)`, `requireSignedConsensusConfig()`, and `isIndexerAddress(address)`.
17
+ * @throws {Error} When consensus schemas cannot be initialized from the configuration.
18
+ */
19
+ constructor(config, state) {
20
+ super(config, state);
21
+ }
22
+
23
+ /**
24
+ * Validates a complete incoming epoch proof proposal request.
25
+ *
26
+ * Checks the payload schema, active consensus version, network id, proposer identity,
27
+ * proposal signature, proposer indexer membership, next epoch number,
28
+ * previous epoch record hash, consensus configuration, and VDF proof.
29
+ *
30
+ * @param {object} payload Decoded proof proposal request payload.
31
+ * @param {object} connection Peer connection containing `remotePublicKey`.
32
+ * @returns {Promise<boolean>} Resolves to `true` when every check succeeds.
33
+ * @throws {V1ConsensusProtocolError} When any proposal validation check fails.
34
+ */
35
+ async validate(payload, connection) {
36
+ return await this.validateAsProtocolError(async () => {
37
+ this.isPayloadSchemaValid(payload);
38
+ this.validateProofProposalNetworkId(payload.proof_proposal);
39
+ this.assertAddressWithRemotePublicKey(
40
+ payload.proof_proposal.proposer,
41
+ connection.remotePublicKey
42
+ );
43
+ await this.validateProofProposalConfig(payload.proof_proposal);
44
+ await this.validateSignature(payload, connection.remotePublicKey, undefined, ConsensusResultCode.PROPOSAL_SIGNATURE_INVALID);
45
+ await this.validateAddressIsIndexer(connection.remotePublicKey);
46
+ this.validateLocalNodeIsIndexer();
47
+ const currentEpoch = await this._state.requireCurrentEpoch();
48
+ this.validateIncomingEpoch(payload.proof_proposal, currentEpoch);
49
+ await this.validatePreviousEpochRecordHash(payload.proof_proposal, currentEpoch);
50
+ await this.validateProofProposalVdfProof(payload.proof_proposal);
51
+
52
+ // The signed config may have changed during VDF verification.
53
+ await this.validateProofProposalConfig(payload.proof_proposal);
54
+ return true;
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Verifies the proof proposal VDF proof against its challenge data.
60
+ *
61
+ * Uses the canonical challenge fields and signed-state VDF difficulty and
62
+ * discriminant size for Wesolowski proof verification.
63
+ *
64
+ * @param {object} proofProposal Decoded proof proposal.
65
+ * @returns {Promise<void>}
66
+ * @throws {V1ConsensusProtocolError} When VDF verification fails or returns false.
67
+ */
68
+ async validateProofProposalVdfProof(proofProposal) {
69
+ const challengeData = this.buildProofProposalChallengeData(proofProposal);
70
+ const difficulty = proofProposal.difficulty.readUInt32BE(0);
71
+ const discriminantBitSize = proofProposal.discriminant_bit_size.readUInt16BE(0);
72
+
73
+ let verified = false;
74
+ try {
75
+ verified = await verifyWesolowski(
76
+ challengeData,
77
+ difficulty,
78
+ proofProposal.proof,
79
+ discriminantBitSize
80
+ );
81
+ } catch {
82
+ verified = false;
83
+ }
84
+
85
+ if (!verified) {
86
+ throw new V1ConsensusProtocolError(
87
+ ConsensusResultCode.VDF_PROOF_INVALID,
88
+ 'VDF proof is invalid.'
89
+ );
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Validates that the proof proposal targets the configured network.
95
+ *
96
+ * @param {object} proofProposal Decoded proof proposal.
97
+ * @returns {void}
98
+ * @throws {V1ConsensusProtocolError} When `network_id` differs from the local network id.
99
+ */
100
+ validateProofProposalNetworkId(proofProposal) {
101
+ const expectedNetworkId = uint16ToBuffer(this._config.networkId);
102
+ if (!b4a.equals(proofProposal.network_id, expectedNetworkId)) {
103
+ throw new V1ConsensusProtocolError(
104
+ ConsensusResultCode.WRONG_NETWORK_ID,
105
+ 'Invalid proof proposal network id.'
106
+ );
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Validates that the proposal advances the current epoch by exactly one.
112
+ *
113
+ * @param {object} proofProposal Decoded proof proposal containing an uint64 epoch buffer.
114
+ * @param {bigint} currentEpoch Current epoch snapshot used by all epoch-related checks.
115
+ * @returns {void}
116
+ * @throws {V1ConsensusProtocolError} When the proposed epoch is not `currentEpoch + 1`.
117
+ */
118
+ validateIncomingEpoch(proofProposal, currentEpoch) {
119
+ const proofProposalEpoch = proofProposal.epoch.readBigUInt64BE(0);
120
+ if (proofProposalEpoch !== currentEpoch + 1n) {
121
+ throw new V1ConsensusProtocolError(
122
+ ConsensusResultCode.EPOCH_INVALID,
123
+ `Unexpected epoch. Proof proposal must be ${currentEpoch + 1n} but got ${proofProposalEpoch}`
124
+ );
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Validates that the proposal references the current epoch record hash.
130
+ *
131
+ * Reads the current epoch id and its stored hash, then compares that hash
132
+ * with `previous_epoch_record_hash` from the proposal.
133
+ *
134
+ * @param {object} proofProposal Decoded proof proposal.
135
+ * @param {bigint} currentEpoch Current epoch snapshot used by all epoch-related checks.
136
+ * @returns {Promise<void>}
137
+ * @throws {V1ConsensusProtocolError} When the previous epoch record hash does not match state.
138
+ */
139
+ async validatePreviousEpochRecordHash(proofProposal, currentEpoch) {
140
+ const currentEpochHash = await this._state.requireEpoch(currentEpoch);
141
+ const epochHashInProofProposal = proofProposal.previous_epoch_record_hash;
142
+ if (!b4a.equals(currentEpochHash, epochHashInProofProposal)) {
143
+ throw new V1ConsensusProtocolError(
144
+ ConsensusResultCode.PREVIOUS_EPOCH_RECORD_HASH_INVALID,
145
+ `Previous epoch record hash mismatch for epoch ${currentEpoch}: expected ${currentEpochHash.toString('hex')}, got ${epochHashInProofProposal.toString('hex')}`
146
+ );
147
+ }
148
+
149
+ }
150
+ }
151
+
152
+ export default V1EpochProofProposalRequest;
@@ -0,0 +1,145 @@
1
+ import b4a from 'b4a';
2
+ import {
3
+ ConsensusResultCode,
4
+ EPOCH_APPENDER_SELECTION_COUNTER_LIMIT,
5
+ EPOCH_BYTE_LENGTH,
6
+ VDF_DISCRIMINANT_SIZE,
7
+ VDF_PROOF_BYTE_LENGTHS,
8
+ } from '../../../../../../utils/constants.js';
9
+
10
+ /**
11
+ * Checks that the proof length matches the discriminant size in the proposal.
12
+ *
13
+ * @param {object} proofProposal Proposal checked by the compiled schema.
14
+ * @param {object[]} errors Validation errors collected by the schema.
15
+ * @returns {object} Original proposal.
16
+ */
17
+ export function validateProofProposalLength(proofProposal, errors) {
18
+ const discriminant = proofProposal?.discriminant_bit_size;
19
+ const proof = proofProposal?.proof;
20
+ if (!b4a.isBuffer(discriminant) || discriminant.length !== VDF_DISCRIMINANT_SIZE) {
21
+ return proofProposal;
22
+ }
23
+
24
+ const discriminantBitSize = discriminant.readUInt16BE(0);
25
+ const expectedProofLength = VDF_PROOF_BYTE_LENGTHS[discriminantBitSize];
26
+ if (!Number.isInteger(expectedProofLength)) {
27
+ errors.push({
28
+ type: 'unsupportedDiscriminantBitSize',
29
+ field: 'proof_proposal.discriminant_bit_size',
30
+ actual: discriminantBitSize,
31
+ expected: Object.keys(VDF_PROOF_BYTE_LENGTHS).map(Number),
32
+ message: 'Discriminant bit size must be one of: 1024, 2048, 4096.'
33
+ });
34
+ return proofProposal;
35
+ }
36
+
37
+ if (b4a.isBuffer(proof) && proof.length !== expectedProofLength) {
38
+ errors.push({
39
+ type: 'bufferLength',
40
+ field: 'proof_proposal.proof',
41
+ actual: proof.length,
42
+ expected: expectedProofLength,
43
+ message: `Proof must be a Buffer with length ${expectedProofLength}.`
44
+ });
45
+ }
46
+ return proofProposal;
47
+ }
48
+
49
+ /**
50
+ * Requires an approval for OK responses and forbids it for other results.
51
+ *
52
+ * @param {object} response Response checked by the compiled schema.
53
+ * @param {object[]} errors Validation errors collected by the schema.
54
+ * @returns {object} Original response.
55
+ */
56
+ export function validateProofProposalApprovalPresence(response, errors) {
57
+ if (!response || typeof response !== 'object') {
58
+ return response;
59
+ }
60
+ const requiresApproval = response.result === ConsensusResultCode.OK;
61
+ const hasApproval = response.approval !== undefined && response.approval !== null;
62
+ if (requiresApproval !== hasApproval) {
63
+ errors.push({
64
+ type: 'conditionalDependency',
65
+ field: 'proof_proposal_response.approval',
66
+ message: requiresApproval
67
+ ? 'Approval is required when the result is OK.'
68
+ : 'Approval is only allowed when the result is OK.'
69
+ });
70
+ }
71
+ return response;
72
+ }
73
+
74
+ /**
75
+ * Checks the allowed counter range after the buffer rule checks its shape.
76
+ *
77
+ * @param {Buffer} counter Encoded election counter.
78
+ * @param {object[]} errors Validation errors collected by the schema.
79
+ * @returns {Buffer} Original counter.
80
+ */
81
+ export function validateSelectionCounter(counter, errors) {
82
+ if (!b4a.isBuffer(counter) || counter.length !== EPOCH_BYTE_LENGTH) {
83
+ return counter;
84
+ }
85
+ const counterValue = counter.readBigUInt64BE(0);
86
+ if (counterValue === 0n || counterValue > EPOCH_APPENDER_SELECTION_COUNTER_LIMIT) {
87
+ errors.push({
88
+ type: 'selectionCounter',
89
+ actual: counterValue.toString(),
90
+ message: 'Selection counter is outside the allowed range.'
91
+ });
92
+ }
93
+ return counter;
94
+ }
95
+
96
+ /**
97
+ * Builds the buffer check during schema compilation. The validator provides this.makeError.
98
+ *
99
+ * @this {import('fastest-validator').default}
100
+ * @param {object} rule Buffer field definition and error messages.
101
+ * @returns {{source: string}} Code used by the compiled validator.
102
+ */
103
+ export function compileBufferRule({ schema, messages }) {
104
+ const isBuffer = b4a.isBuffer;
105
+ const allowEmpty = schema.allowEmpty === true;
106
+ const exactLength = Number.isInteger(schema.length) ? schema.length : null;
107
+ const minLength = Number.isInteger(schema.min) ? schema.min : null;
108
+ const maxLength = Number.isInteger(schema.max) ? schema.max : null;
109
+ return {
110
+ source: `
111
+ if (!${isBuffer}(value)) {
112
+ ${this.makeError({ type: "buffer", actual: "value", messages })}
113
+ return value;
114
+ }
115
+ const len = value.length;
116
+ ${exactLength === null ? '' : `
117
+ if (len !== ${exactLength}) {
118
+ ${this.makeError({ type: "bufferLength", expected: exactLength, actual: "len", messages })}
119
+ }`}
120
+ ${minLength === null ? '' : `
121
+ if (len < ${minLength}) {
122
+ ${this.makeError({ type: "bufferMinLength", expected: minLength, actual: "len", messages })}
123
+ }`}
124
+ ${maxLength === null ? '' : `
125
+ if (len > ${maxLength}) {
126
+ ${this.makeError({ type: "bufferMaxLength", expected: maxLength, actual: "len", messages })}
127
+ }`}
128
+ if (len === 0 && !${allowEmpty}) {
129
+ ${this.makeError({ type: "emptyBuffer", actual: "len", messages })}
130
+ return value;
131
+ }
132
+ let isZeroFilled = true;
133
+ for (let i = 0; i < len; i++) {
134
+ if (value[i] !== 0) {
135
+ isZeroFilled = false;
136
+ break;
137
+ }
138
+ }
139
+ if (isZeroFilled) {
140
+ ${this.makeError({ type: "nonZeroBuffer", actual: "value", messages })}
141
+ }
142
+ return value;
143
+ `,
144
+ };
145
+ }
@@ -0,0 +1,27 @@
1
+ import {publicKeyToAddress} from "../../../../utils/helpers.js";
2
+
3
+ export default class ConnectionOperationHandler {
4
+ #config;
5
+
6
+ constructor(config) {
7
+ this.#config = config;
8
+ }
9
+
10
+ get config() {
11
+ return this.#config;
12
+ }
13
+
14
+ async sendResponseAndMaybeClose(protocolSession, connection, response, endConnection = true) {
15
+ protocolSession.sendAndForget(response);
16
+ if (!endConnection) return;
17
+
18
+ await connection.flush();
19
+ connection.end();
20
+ }
21
+
22
+ displayError(step = "undefined step", senderPublicKey, error) {
23
+ const errorMessage = error?.message ?? 'Unexpected error';
24
+ console.error(`${this.constructor.name}: ${step} ${publicKeyToAddress(senderPublicKey, this.#config)}: ${errorMessage}`);
25
+ }
26
+
27
+ }
@@ -1,10 +1,10 @@
1
- import NetworkMessageRouter from './legacy/NetworkMessageRouter.js';
2
- import NetworkMessageRouterV1 from './v1/NetworkMessageRouter.js';
3
- import ProtocolSession from './ProtocolSession.js';
4
- import LegacyProtocol from './LegacyProtocol.js';
5
- import V1Protocol from './V1Protocol.js';
1
+ import ValidatorLegacyMessageRouter from './legacy/ValidatorLegacyMessageRouter.js';
2
+ import ValidatorV1MessageRouter from './v1/ValidatorV1MessageRouter.js';
3
+ import ValidatorProtocolSession from './ValidatorProtocolSession.js';
4
+ import ValidatorLegacyProtocol from './legacy/ValidatorLegacyProtocol.js';
5
+ import ValidatorV1Protocol from './v1/ValidatorV1Protocol.js';
6
6
 
7
- class NetworkMessages {
7
+ class ValidatorMessages {
8
8
  #legacyMessageRouter;
9
9
  #v1MessageRouter;
10
10
  #config;
@@ -23,7 +23,7 @@ class NetworkMessages {
23
23
  this.#config = config;
24
24
  this.#wallet = wallet;
25
25
  this.#pendingRequestsService = pendingRequestsService;
26
- this.#legacyMessageRouter = new NetworkMessageRouter(
26
+ this.#legacyMessageRouter = new ValidatorLegacyMessageRouter(
27
27
  state,
28
28
  wallet,
29
29
  rateLimiterService,
@@ -31,7 +31,7 @@ class NetworkMessages {
31
31
  this.#config
32
32
  );
33
33
 
34
- this.#v1MessageRouter = new NetworkMessageRouterV1(
34
+ this.#v1MessageRouter = new ValidatorV1MessageRouter(
35
35
  state,
36
36
  wallet,
37
37
  rateLimiterService,
@@ -42,28 +42,38 @@ class NetworkMessages {
42
42
  );
43
43
  }
44
44
 
45
- async setupProtomuxMessages(connection) {
45
+ createProtocolSession(connection) {
46
46
  // Attach a Protomux instance to this Hyperswarm connection.
47
47
  // Protomux multiplexes multiple logical protocol channels over a single encrypted stream.
48
48
 
49
- const legacyProtocol = new LegacyProtocol(
49
+ const legacyProtocol = new ValidatorLegacyProtocol(
50
50
  this.#legacyMessageRouter,
51
51
  connection,
52
52
  null,
53
53
  this.#config
54
54
  );
55
55
 
56
- const v1Protocol = new V1Protocol(
56
+ const v1Protocol = new ValidatorV1Protocol(
57
57
  this.#v1MessageRouter,
58
58
  connection,
59
59
  this.#pendingRequestsService,
60
60
  this.#config
61
61
  );
62
62
 
63
- // ProtocolSession is attached to the Hyperswarm connection so other parts of the system (e.g. tryConnect)
63
+ // ValidatorProtocolSession is attached to the Hyperswarm connection so other parts of the system (e.g. tryConnect)
64
64
  // can send messages without knowing how Protomux was initialized.
65
- connection.protocolSession = new ProtocolSession(legacyProtocol, v1Protocol, this.#wallet, this.#config);
65
+ return new ValidatorProtocolSession(legacyProtocol, v1Protocol, this.#wallet, this.#config);
66
+ }
67
+
68
+ attachChannel(connection) {
69
+ connection.protocolSessions ??= {};
70
+ if (connection.protocolSessions.validator) return;
71
+ connection.protocolSessions.validator = this.createProtocolSession(connection);
72
+ }
73
+
74
+ prepareConnection(connection) {
75
+ this.attachChannel(connection);
66
76
  }
67
77
  }
68
78
 
69
- export default NetworkMessages;
79
+ export default ValidatorMessages;
@@ -1,15 +1,15 @@
1
- // ProtocolSession is a per-peer (per Hyperswarm connection) bridge that exposes the available
1
+ // ValidatorProtocolSession is a per-peer (per Hyperswarm connection) bridge that exposes the available
2
2
  // protocol messengers (legacy JSON, v1 binary) in one place.
3
3
  //
4
4
  // Why it exists:
5
5
  // - `setupProtomuxMessages(connection)` creates Protomux channels/messages for a specific peer.
6
6
 
7
- import { networkMessageFactory } from '../../../messages/network/v1/networkMessageFactory.js';
8
- import { generateUUID } from '../../../utils/helpers.js';
9
- import { NETWORK_CAPABILITIES, ResultCode } from '../../../utils/constants.js';
10
- import { Logger } from '../../../utils/logger.js';
7
+ import { networkMessageFactory } from '../../../../messages/network/v1/networkMessageFactory.js';
8
+ import { generateUUID } from '../../../../utils/helpers.js';
9
+ import { NETWORK_CAPABILITIES, ResultCode } from '../../../../utils/constants.js';
10
+ import { Logger } from '../../../../utils/logger.js';
11
11
 
12
- class ProtocolSession {
12
+ class ValidatorProtocolSession {
13
13
  #legacyProtocol;
14
14
  #v1Protocol;
15
15
  #preferredProtocol = null;
@@ -51,24 +51,24 @@ class ProtocolSession {
51
51
  setLegacyAsPreferredProtocol() {
52
52
  if (this.isProbed()) {
53
53
  // TODO: SOMETIMES WE ARE PROBING NODE FOR MULTIPLE AMOUNT OF TIME, THIS IS BAD. WE NEED TO INVESTIGATE THIS.
54
- //this.#logger.warn(`ProtocolSession: preferred protocol is already set and cannot be changed to LEGACY. Current preferred protocol: ${this.#preferredProtocol}`);
54
+ //this.#logger.warn(`ValidatorProtocolSession: preferred protocol is already set and cannot be changed to LEGACY. Current preferred protocol: ${this.#preferredProtocol}`);
55
55
  return;
56
56
  }
57
57
  this.#preferredProtocol = this.#supportedProtocols.LEGACY;
58
58
  this.#activeProtocol = this.#legacyProtocol;
59
- this.#logger.debug('ProtocolSession: set preferred protocol to LEGACY');
59
+ this.#logger.debug('ValidatorProtocolSession: set preferred protocol to LEGACY');
60
60
  }
61
61
 
62
62
  setV1AsPreferredProtocol() {
63
63
  if (this.isProbed()) {
64
64
  // TODO: SOMETIMES WE ARE PROBING NODE FOR MULTIPLE AMOUNT OF TIME, THIS IS BAD. WE NEED TO INVESTIGATE THIS.
65
- //this.#logger.warn(`ProtocolSession: preferred protocol is already set and cannot be changed to V1. Current preferred protocol: ${this.#preferredProtocol}`);
65
+ //this.#logger.warn(`ValidatorProtocolSession: preferred protocol is already set and cannot be changed to V1. Current preferred protocol: ${this.#preferredProtocol}`);
66
66
  return;
67
67
  }
68
68
 
69
69
  this.#preferredProtocol = this.#supportedProtocols.V1;
70
70
  this.#activeProtocol = this.#v1Protocol;
71
- this.#logger.debug('ProtocolSession: set preferred protocol to V1');
71
+ this.#logger.debug('ValidatorProtocolSession: set preferred protocol to V1');
72
72
  }
73
73
 
74
74
  /**
@@ -83,25 +83,25 @@ class ProtocolSession {
83
83
  */
84
84
  async probe() {
85
85
  if (this.isProbed()) {
86
- this.#logger.warn(`ProtocolSession: preferred protocol is already set. Skipping probe. Current preferred protocol: ${this.#preferredProtocol}`);
86
+ this.#logger.warn(`ValidatorProtocolSession: preferred protocol is already set. Skipping probe. Current preferred protocol: ${this.#preferredProtocol}`);
87
87
  return; // TODO: Consider not returning silently
88
88
  }
89
89
 
90
90
  try {
91
91
  const message = await this.#buildLivenessRequest();
92
92
  if (!this.#v1Protocol) {
93
- throw new Error('ProtocolSession: v1 protocol not available for probing');
93
+ throw new Error('ValidatorProtocolSession: v1 protocol not available for probing');
94
94
  }
95
95
  const result = await this.#v1Protocol.send(message);
96
96
  if (result !== ResultCode.OK) {
97
97
  // TODO: Think about how to handle failure result codes after legacy protocol is retired
98
- this.#logger.warn(`ProtocolSession: v1 protocol probe failed with non-OK result code: ${result}`);
98
+ this.#logger.warn(`ValidatorProtocolSession: v1 protocol probe failed with non-OK result code: ${result}`);
99
99
  this.setLegacyAsPreferredProtocol();
100
100
  return;
101
101
  }
102
102
  this.setV1AsPreferredProtocol();
103
103
  } catch (err) {
104
- this.#logger.debug(`ProtocolSession: v1 protocol probe failed, falling back to legacy. Details: ${err?.message ?? err}`);
104
+ this.#logger.debug(`ValidatorProtocolSession: v1 protocol probe failed, falling back to legacy. Details: ${err?.message ?? err}`);
105
105
  this.setLegacyAsPreferredProtocol();
106
106
  }
107
107
  }
@@ -119,14 +119,14 @@ class ProtocolSession {
119
119
  return await this.#v1Protocol.send(message);
120
120
  }
121
121
  catch (err) {
122
- this.#logger.error(`ProtocolSession: v1 health check failed: ${err?.message ?? err}`);
122
+ this.#logger.error(`ValidatorProtocolSession: v1 health check failed: ${err?.message ?? err}`);
123
123
  return ResultCode.UNEXPECTED_ERROR; // TODO: Consider just propagating the error instead
124
124
  }
125
125
  case this.#supportedProtocols.LEGACY:
126
- this.#logger.warn('ProtocolSession: health check not supported on LEGACY protocol');
126
+ this.#logger.warn('ValidatorProtocolSession: health check not supported on LEGACY protocol');
127
127
  return ResultCode.OK; // TODO: Consider implementing a new result code (e.g. NOT_SUPPORTED) instead of returning OK
128
128
  default:
129
- this.#logger.warn('ProtocolSession: preferred protocol not set. Call probe() first.');
129
+ this.#logger.warn('ValidatorProtocolSession: preferred protocol not set. Call probe() first.');
130
130
  return ResultCode.UNSPECIFIED; // TODO: Define a more specific result code.
131
131
  }
132
132
  }
@@ -136,10 +136,7 @@ class ProtocolSession {
136
136
  * @returns {Boolean} True if health checks are supported in the preferred protocol, false otherwise.
137
137
  */
138
138
  isHealthCheckSupported() {
139
- if (this.#preferredProtocol === null) {
140
- throw new Error('ProtocolSession: preferred protocol not set. Call probe() first.');
141
- }
142
- return this.#preferredProtocol === this.#supportedProtocols.V1;
139
+ return this.isProbed() && this.#preferredProtocol === this.#supportedProtocols.V1;
143
140
  }
144
141
 
145
142
  // TODO: Consider moving this method to be used only in V1 internally, just like 'encode'
@@ -160,7 +157,7 @@ class ProtocolSession {
160
157
  try {
161
158
  this.#legacyProtocol.close();
162
159
  } catch (e) {
163
- this.#logger.error(`ProtocolSession: failed to close legacy channel: ${e?.message ?? e}`); // TODO: Think about throwing instead
160
+ this.#logger.error(`ValidatorProtocolSession: failed to close legacy channel: ${e?.message ?? e}`); // TODO: Think about throwing instead
164
161
  }
165
162
  }
166
163
 
@@ -168,14 +165,14 @@ class ProtocolSession {
168
165
  try {
169
166
  this.#v1Protocol.close();
170
167
  } catch (e) {
171
- this.#logger.error(`ProtocolSession: failed to close v1 channel: ${e?.message ?? e}`); // TODO: Think about throwing instead
168
+ this.#logger.error(`ValidatorProtocolSession: failed to close v1 channel: ${e?.message ?? e}`); // TODO: Think about throwing instead
172
169
  }
173
170
  }
174
171
  }
175
172
 
176
173
  async #buildLivenessRequest() {
177
174
  if (!this.#wallet || !this.#config) {
178
- throw new Error('ProtocolSession: wallet/config not set for liveness request');
175
+ throw new Error('ValidatorProtocolSession: wallet/config not set for liveness request');
179
176
  }
180
177
  const requestId = generateUUID();
181
178
  return await networkMessageFactory(this.#wallet, this.#config)
@@ -183,4 +180,4 @@ class ProtocolSession {
183
180
  }
184
181
  }
185
182
 
186
- export default ProtocolSession;
183
+ export default ValidatorProtocolSession;
@@ -5,10 +5,10 @@ import LegacyResponseHandler from "./handlers/LegacyResponseHandler.js";
5
5
  import LegacyRoleOperationHandler from "./handlers/LegacyRoleOperationHandler.js";
6
6
  import LegacySubnetworkOperationHandler from "./handlers/LegacySubnetworkOperationHandler.js";
7
7
  import LegacyTransferOperationHandler from "./handlers/LegacyTransferOperationHandler.js";
8
- import { NETWORK_MESSAGE_TYPES } from '../../../../utils/constants.js';
9
- import * as operation from '../../../../utils/applyOperations.js';
8
+ import { NETWORK_MESSAGE_TYPES } from '../../../../../utils/constants.js';
9
+ import * as operation from '../../../../../utils/applyOperations.js';
10
10
 
11
- class NetworkMessageRouter {
11
+ class ValidatorLegacyMessageRouter {
12
12
  #handlers;
13
13
  #config;
14
14
 
@@ -37,9 +37,9 @@ class NetworkMessageRouter {
37
37
  const channelString = b4a.toString(this.#config.channel, 'utf8');
38
38
 
39
39
  // We received a legacy message, so we set the connection protocol accordingly
40
- connection.protocolSession.setLegacyAsPreferredProtocol();
40
+ connection.protocolSessions.validator.setLegacyAsPreferredProtocol();
41
41
  if (this.#isGetRequest(incomingMessage)) {
42
- await this.#handlers.get.handle(incomingMessage, connection, channelString);
42
+ await this.#handlers.get.handle(incomingMessage, connection, connection.protocolSessions.validator, channelString);
43
43
  }
44
44
  else if (this.#isResponse(incomingMessage)) {
45
45
  await this.#handlers.response.handle(incomingMessage, channelString);
@@ -87,4 +87,4 @@ class NetworkMessageRouter {
87
87
  }
88
88
 
89
89
 
90
- export default NetworkMessageRouter;
90
+ export default ValidatorLegacyMessageRouter;
@@ -1,9 +1,9 @@
1
1
  import Protomux from 'protomux';
2
- import ProtocolInterface from './ProtocolInterface.js';
2
+ import ValidatorProtocolInterface from '../shared/ValidatorProtocolInterface.js';
3
3
  import b4a from 'b4a';
4
4
  import c from 'compact-encoding';
5
5
 
6
- class LegacyProtocol extends ProtocolInterface {
6
+ class ValidatorLegacyProtocol extends ValidatorProtocolInterface {
7
7
  #channel;
8
8
  #session;
9
9
  #config;
@@ -35,6 +35,8 @@ class LegacyProtocol extends ProtocolInterface {
35
35
  onclose() { }
36
36
  });
37
37
 
38
+ if (!this.#channel) return; // connection already destroyed before protocol setup completed
39
+
38
40
  this.#channel.open();
39
41
 
40
42
  // Todo: Abstract in a separate function
@@ -42,7 +44,7 @@ class LegacyProtocol extends ProtocolInterface {
42
44
  encoding: c.json,
43
45
  onmessage: async (incomingMessage) => {
44
46
  this.#router.route(incomingMessage, connection).catch((err) => {
45
- console.error(`LegacyProtocol: unhandled router error: ${err.message}`);
47
+ console.error(`ValidatorLegacyProtocol: unhandled router error: ${err.message}`);
46
48
  try {
47
49
  connection.end();
48
50
  } catch {
@@ -61,17 +63,17 @@ class LegacyProtocol extends ProtocolInterface {
61
63
  async send(message) {
62
64
  this.sendAndForget(message);
63
65
  // TODO: Change 'null' to an appropriate response if needed in the future
64
- return Promise.resolve(null); // This is to maintain consistency with the ProtocolInterface and v1 protocol.
66
+ return Promise.resolve(null); // This is to maintain consistency with the ValidatorProtocolInterface and v1 protocol.
65
67
  }
66
68
 
67
69
  sendAndForget(message) {
68
- this.#session.send(message);
70
+ this.#session?.send(message);
69
71
  }
70
72
 
71
73
  close() {
72
- this.#channel.close();
74
+ this.#channel?.close();
73
75
  }
74
76
 
75
77
  }
76
78
 
77
- export default LegacyProtocol;
79
+ export default ValidatorLegacyProtocol;
@@ -1,5 +1,5 @@
1
1
  import b4a from 'b4a';
2
- import {publicKeyToAddress} from "../../../../../utils/helpers.js";
2
+ import {publicKeyToAddress} from "../../../../../../utils/helpers.js";
3
3
 
4
4
  class BaseStateOperationHandler {
5
5
  #state;