trac-msb 0.2.12 → 0.2.13

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 (114) hide show
  1. package/package.json +9 -4
  2. package/proto/network/v1/enums/message_type.proto +16 -0
  3. package/proto/network/v1/enums/result_code.proto +84 -0
  4. package/proto/network/v1/messages/broadcast_transaction_request.proto +9 -0
  5. package/proto/network/v1/messages/broadcast_transaction_response.proto +13 -0
  6. package/proto/network/v1/messages/liveness_request.proto +8 -0
  7. package/proto/network/v1/messages/liveness_response.proto +11 -0
  8. package/proto/network/v1/network_message.proto +22 -0
  9. package/rpc/rpc_services.js +22 -4
  10. package/scripts/generate-protobufs.js +37 -12
  11. package/src/config/config.js +26 -5
  12. package/src/config/env.js +25 -11
  13. package/src/core/network/Network.js +73 -36
  14. package/src/core/network/protocols/LegacyProtocol.js +21 -11
  15. package/src/core/network/protocols/NetworkMessages.js +38 -17
  16. package/src/core/network/protocols/ProtocolInterface.js +14 -2
  17. package/src/core/network/protocols/ProtocolSession.js +144 -17
  18. package/src/core/network/protocols/V1Protocol.js +37 -18
  19. package/src/core/network/protocols/connectionPolicies.js +88 -0
  20. package/src/core/network/protocols/legacy/NetworkMessageRouter.js +25 -19
  21. package/src/core/network/protocols/{shared/handlers/base/BaseOperationHandler.js → legacy/handlers/BaseStateOperationHandler.js} +23 -12
  22. package/src/core/network/protocols/legacy/handlers/{GetRequestHandler.js → LegacyGetRequestHandler.js} +6 -6
  23. package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +23 -0
  24. package/src/core/network/protocols/{shared/handlers/RoleOperationHandler.js → legacy/handlers/LegacyRoleOperationHandler.js} +18 -11
  25. package/src/core/network/protocols/{shared/handlers/SubnetworkOperationHandler.js → legacy/handlers/LegacySubnetworkOperationHandler.js} +28 -17
  26. package/src/core/network/protocols/{shared/handlers/TransferOperationHandler.js → legacy/handlers/LegacyTransferOperationHandler.js} +17 -11
  27. package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +27 -0
  28. package/src/core/network/protocols/shared/validators/{PartialBootstrapDeployment.js → PartialBootstrapDeploymentValidator.js} +9 -4
  29. package/src/core/network/protocols/shared/validators/{base/PartialOperation.js → PartialOperationValidator.js} +47 -25
  30. package/src/core/network/protocols/shared/validators/{PartialRoleAccess.js → PartialRoleAccessValidator.js} +51 -17
  31. package/src/core/network/protocols/shared/validators/{PartialTransaction.js → PartialTransactionValidator.js} +21 -7
  32. package/src/core/network/protocols/shared/validators/{PartialTransfer.js → PartialTransferValidator.js} +26 -9
  33. package/src/core/network/protocols/v1/NetworkMessageRouter.js +91 -7
  34. package/src/core/network/protocols/v1/V1ProtocolError.js +91 -0
  35. package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +65 -0
  36. package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +389 -0
  37. package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +87 -0
  38. package/src/core/network/protocols/v1/validators/V1BaseOperation.js +211 -0
  39. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +26 -0
  40. package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +276 -0
  41. package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +15 -0
  42. package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +17 -0
  43. package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +210 -0
  44. package/src/core/network/services/ConnectionManager.js +146 -94
  45. package/src/core/network/services/MessageOrchestrator.js +151 -27
  46. package/src/core/network/services/PendingRequestService.js +172 -0
  47. package/src/core/network/services/TransactionCommitService.js +149 -0
  48. package/src/core/network/services/TransactionPoolService.js +129 -18
  49. package/src/core/network/services/TransactionRateLimiterService.js +52 -34
  50. package/src/core/network/services/ValidatorHealthCheckService.js +127 -0
  51. package/src/core/network/services/ValidatorObserverService.js +18 -26
  52. package/src/core/state/State.js +70 -19
  53. package/src/index.js +5 -4
  54. package/src/messages/network/v1/NetworkMessageBuilder.js +59 -79
  55. package/src/messages/network/v1/NetworkMessageDirector.js +16 -50
  56. package/src/utils/Scheduler.js +0 -8
  57. package/src/utils/constants.js +71 -5
  58. package/src/utils/deepEqualApplyPayload.js +40 -0
  59. package/src/utils/helpers.js +10 -1
  60. package/src/utils/logger.js +25 -0
  61. package/src/utils/normalizers.js +38 -0
  62. package/src/utils/protobuf/networkV1.generated.cjs +2460 -0
  63. package/src/utils/protobuf/operationHelpers.js +24 -3
  64. package/tests/acceptance/v1/account/account.test.mjs +8 -2
  65. package/tests/acceptance/v1/tx/tx.test.mjs +23 -1
  66. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +34 -6
  67. package/tests/fixtures/networkV1.fixtures.js +2 -28
  68. package/tests/helpers/transactionPayloads.mjs +2 -2
  69. package/tests/unit/messages/network/NetworkMessageBuilder.test.js +239 -79
  70. package/tests/unit/messages/network/NetworkMessageDirector.test.js +223 -77
  71. package/tests/unit/network/LegacyNetworkMessageRouter.test.js +54 -0
  72. package/tests/unit/network/ProtocolSession.test.js +127 -0
  73. package/tests/unit/network/networkModule.test.js +4 -1
  74. package/tests/unit/network/services/ConnectionManager.test.js +450 -0
  75. package/tests/unit/network/services/MessageOrchestrator.test.js +445 -0
  76. package/tests/unit/network/services/PendingRequestService.test.js +431 -0
  77. package/tests/unit/network/services/TransactionCommitService.test.js +246 -0
  78. package/tests/unit/network/services/TransactionPoolService.test.js +489 -0
  79. package/tests/unit/network/services/TransactionRateLimiterService.test.js +139 -0
  80. package/tests/unit/network/services/ValidatorHealthCheckService.test.js +115 -0
  81. package/tests/unit/network/services/services.test.js +17 -0
  82. package/tests/unit/network/utils/v1TestUtils.js +153 -0
  83. package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +151 -0
  84. package/tests/unit/network/v1/V1BaseOperation.test.js +356 -0
  85. package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +129 -0
  86. package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +53 -0
  87. package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +512 -0
  88. package/tests/unit/network/v1/V1LivenessRequest.test.js +32 -0
  89. package/tests/unit/network/v1/V1LivenessResponse.test.js +45 -0
  90. package/tests/unit/network/v1/V1ResultCode.test.js +84 -0
  91. package/tests/unit/network/v1/V1ValidationSchema.test.js +13 -0
  92. package/tests/unit/network/v1/connectionPolicies.test.js +49 -0
  93. package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +284 -0
  94. package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +794 -0
  95. package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +193 -0
  96. package/tests/unit/network/v1/v1.handlers.test.js +15 -0
  97. package/tests/unit/network/v1/v1.test.js +19 -0
  98. package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +119 -0
  99. package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +136 -0
  100. package/tests/unit/network/v1/v1ValidationSchema/common.test.js +308 -0
  101. package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +90 -0
  102. package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +133 -0
  103. package/tests/unit/unit.test.js +2 -2
  104. package/tests/unit/utils/deepEqualApplyPayload/deepEqualApplyPayload.test.js +102 -0
  105. package/tests/unit/utils/protobuf/operationHelpers.test.js +2 -4
  106. package/tests/unit/utils/utils.test.js +1 -0
  107. package/.github/workflows/acceptance-tests.yml +0 -38
  108. package/.github/workflows/lint-pr-title.yml +0 -26
  109. package/.github/workflows/publish.yml +0 -33
  110. package/.github/workflows/unit-tests.yml +0 -34
  111. package/proto/network.proto +0 -74
  112. package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +0 -37
  113. package/src/utils/protobuf/network.cjs +0 -840
  114. package/tests/unit/network/ConnectionManager.test.js +0 -191
@@ -40,13 +40,15 @@ import { safeWriteUInt32BE } from '../../utils/buffer.js';
40
40
  import deploymentEntryUtils from './utils/deploymentEntry.js';
41
41
  import { deepCopyBuffer } from '../../utils/buffer.js';
42
42
  import { Status } from './utils/transaction.js';
43
- import Corestore from 'corestore';
43
+ import remote from 'hypercore/lib/fully-remote-proof.js'
44
+ import PQueue from 'p-queue';
44
45
 
45
46
  const OVERSIZED_BATCH_PENALTY_MULTIPLIER = BATCH_SIZE;
46
47
 
47
48
  // TODO: #addWriter, #removeWriter, #transfer, #transferFeeTxOperation need to be refactored to get in arguments actor's nodeEntries in buffer format.
48
49
 
49
50
  class State extends ReadyResource {
51
+ #writeQueue = new PQueue({ concurrency: 1 });
50
52
  #base;
51
53
  #bee;
52
54
  #store;
@@ -187,18 +189,6 @@ class State extends ReadyResource {
187
189
  return !!nodeEntry.isWhitelisted;
188
190
  }
189
191
 
190
- async isAddressWriter(address) {
191
- const nodeEntry = await this.getNodeEntry(address);
192
- if (nodeEntry === null) return false;
193
- return !!nodeEntry.isWriter;
194
- }
195
-
196
- async isAddressIndexer(address) {
197
- const nodeEntry = await this.getNodeEntry(address);
198
- if (nodeEntry === null) return false;
199
- return !!nodeEntry.isIndexer;
200
- }
201
-
202
192
  async getIndexersEntry() {
203
193
  return Object.values(this.#base.system.indexers);
204
194
  }
@@ -243,7 +233,67 @@ class State extends ReadyResource {
243
233
  }
244
234
 
245
235
  async append(payload) {
246
- await this.#base.append(payload);
236
+ return this.#writeQueue.add(() => this.#base.append(payload));
237
+ }
238
+
239
+ async appendWithProofOfPublication(batch, batchTxHashes) {
240
+ return this.#writeQueue.add(async () => {
241
+
242
+ const core = this.#base.local;
243
+ const end = await this.#base.append(batch);
244
+ const start = end - batch.length;
245
+ const timestamp = new Date();
246
+ const snapshot = core.snapshot(); // consistent view while generating proofs.
247
+ await snapshot.ready();
248
+ // TODO: check state if specific tx has been appened THEN generate a proof.
249
+ try {
250
+ const receipts = [];
251
+ let failedProofs = 0;
252
+ for (let i = 0; i < batch.length; i++) {
253
+ const blockNumber = start + i;
254
+ const completeTx = batch[i];
255
+ const txHash = batchTxHashes[i];
256
+
257
+ let proof = null;
258
+ let proofError = null;
259
+
260
+ // wait:false makes get fail fast (null) instead of waiting for missing data/replication.
261
+ const rawBlock = await snapshot.get(blockNumber, { raw: true, wait: false });
262
+ if (!rawBlock) {
263
+ proofError = `Missing raw block after append (block=${blockNumber}, start=${start}, end=${end})`;
264
+ failedProofs++;
265
+ } else {
266
+ try {
267
+ proof = await remote.proof(snapshot, { index: blockNumber, block: rawBlock });
268
+ } catch (error) {
269
+ proofError = `Proof generation failed (block=${blockNumber}, start=${start}, end=${end}): ${error?.message ?? 'unknown error'}`;
270
+ failedProofs++;
271
+ }
272
+ }
273
+ receipts.push({
274
+ txHash,
275
+ completeTx,
276
+ proof,
277
+ proofError,
278
+ timestamp,
279
+ blockNumber
280
+ });
281
+ }
282
+ if (failedProofs > 0) {
283
+ console.error(`appendWithProof completed with ${failedProofs} proof failures (batch=${batch.length})`);
284
+ }
285
+ return receipts;
286
+ } finally {
287
+ await snapshot.close();
288
+ }
289
+ });
290
+ }
291
+
292
+ async verifyProofOfPublication(proof) {
293
+ // Valid concern. We currently rely on Hypercore’s internal fully-remote-proof helper, which requires low-level storage access
294
+ const out = await remote.verify(this.#store.storage, proof);
295
+ if (!out) throw new Error('Proof of publication verification failed');
296
+ return out;
247
297
  }
248
298
 
249
299
  async getIndexerSequenceState() {
@@ -264,6 +314,7 @@ class State extends ReadyResource {
264
314
  return b4a.equals(initialization, safeWriteUInt32BE(0, 0))
265
315
  }
266
316
  }
317
+
267
318
  async getTransactionConfirmedLength(hash) {
268
319
  if (!isHexString(hash) || hash.length !== 64) {
269
320
  throw new Error("Invalid hash format");
@@ -1793,9 +1844,9 @@ class State extends ReadyResource {
1793
1844
  };
1794
1845
 
1795
1846
  /**
1796
- * Ensure that:
1797
- * 1) writer key exists in registry (we can not unregister something that was not registered),
1798
- * 2) matches the one in node entry ,
1847
+ * Ensure that:
1848
+ * 1) writer key exists in registry (we can not unregister something that was not registered),
1849
+ * 2) matches the one in node entry ,
1799
1850
  * 3) belongs to the requester - this prevents unauthorized key removal
1800
1851
  */
1801
1852
  const writerKeyHasBeenRegistered = await this.#getRegisteredWriterKeyApply(batch, op.rao.iw.toString('hex'))
@@ -2930,7 +2981,7 @@ class State extends ReadyResource {
2930
2981
  batch,
2931
2982
  node
2932
2983
  );
2933
-
2984
+
2934
2985
  // TODO: cover next 4 guards below with tests
2935
2986
  if (transferFeeTxOperationResult === null) {
2936
2987
  this.#safeLogApply(OperationType.TX, "Fee transfer operation failed completely.", node.from.key);
@@ -3388,7 +3439,7 @@ class State extends ReadyResource {
3388
3439
 
3389
3440
  /**
3390
3441
  * Retrieves the address assigned to a given writing key from the registry.
3391
- *
3442
+ *
3392
3443
  * @param {Object} batch - The current Hyperbee batch instance used for reading state.
3393
3444
  * @param {string} writingKey - The writing key in hex string format.
3394
3445
  * @returns {Buffer|null} The address buffer assigned to the writing key, or null if not registered.
package/src/index.js CHANGED
@@ -44,6 +44,7 @@ import {
44
44
  getLicenseCountCommand
45
45
  } from "./utils/cliCommands.js";
46
46
  import {safeEncodeApplyOperation} from "./utils/protobuf/operationHelpers.js";
47
+ import {Config} from "./config/config.js";
47
48
 
48
49
  export class MainSettlementBus extends ReadyResource {
49
50
  #store;
@@ -303,7 +304,7 @@ export class MainSettlementBus extends ReadyResource {
303
304
  const success = await this.broadcastPartialTransaction(adminRecoveryMessage);
304
305
 
305
306
  if (!success) {
306
- throw new Error("Failed to broadcast transaction after multiple attempts.");
307
+ throw new Error("Failed to broadcast transaction. Try again later.");
307
308
  }
308
309
 
309
310
  console.info(`Transaction hash: ${adminRecoveryMessage.rao.tx}`);
@@ -424,7 +425,7 @@ export class MainSettlementBus extends ReadyResource {
424
425
  const success = await this.broadcastPartialTransaction(assembledMessage);
425
426
 
426
427
  if (!success) {
427
- throw new Error("Failed to broadcast transaction after multiple attempts.");
428
+ throw new Error("Failed to broadcast transaction. Try again later.");
428
429
  }
429
430
 
430
431
  console.info(`Transaction hash: ${assembledMessage.rao.tx}`);
@@ -458,7 +459,7 @@ export class MainSettlementBus extends ReadyResource {
458
459
  const success = await this.broadcastPartialTransaction(assembledMessage);
459
460
 
460
461
  if (!success) {
461
- throw new Error("Failed to broadcast transaction after multiple attempts.");
462
+ throw new Error("Failed to broadcast transaction. Try again later.");
462
463
  }
463
464
 
464
465
  console.info(`Transaction hash: ${assembledMessage.rao.tx}`);
@@ -682,7 +683,7 @@ export class MainSettlementBus extends ReadyResource {
682
683
  const success = await this.broadcastPartialTransaction(payload);
683
684
 
684
685
  if (!success) {
685
- throw new Error("Failed to broadcast transaction after multiple attempts.");
686
+ throw new Error("Failed to broadcast transaction. Try again later.");
686
687
  }
687
688
 
688
689
  console.info(`Transaction hash: ${payload.bdo.tx}`);
@@ -19,6 +19,8 @@ class NetworkMessageBuilder {
19
19
  #issuerAddress;
20
20
  #resultCode;
21
21
  #data;
22
+ #proof;
23
+ #timestamp_ledger;
22
24
  #header;
23
25
  #payloadKey;
24
26
  #body;
@@ -85,6 +87,9 @@ class NetworkMessageBuilder {
85
87
  }
86
88
 
87
89
  setData(data) {
90
+ // case when response have to be empty.
91
+ if (data === undefined || data === null) data = b4a.alloc(0);
92
+
88
93
  if (!b4a.isBuffer(data)) {
89
94
  throw new Error(`Data must be a buffer.`);
90
95
  }
@@ -92,6 +97,30 @@ class NetworkMessageBuilder {
92
97
  return this;
93
98
  }
94
99
 
100
+ setProof(proof) {
101
+ if (proof === undefined || proof === null) proof = b4a.alloc(0);
102
+ if (!b4a.isBuffer(proof)) {
103
+ throw new Error(`Proof must be a buffer.`);
104
+ }
105
+ this.#proof = proof;
106
+ return this;
107
+ }
108
+
109
+ setTimestampLedger(timestamp) {
110
+ if (timestamp === undefined || timestamp === null) {
111
+ this.#timestamp_ledger = null;
112
+ return this;
113
+ }
114
+
115
+ const value = timestamp instanceof Date ? timestamp.getTime() : timestamp;
116
+ if (!Number.isSafeInteger(value) || value < 0) {
117
+ throw new Error('timestamp must be a non-negative safe integer or Date.');
118
+ }
119
+
120
+ this.#timestamp_ledger = value;
121
+ return this;
122
+ }
123
+
95
124
  #setHeader() {
96
125
  if (!this.#type) throw new Error('Header requires type to be set');
97
126
  if (!this.#id) throw new Error('Header requires id to be set');
@@ -107,76 +136,6 @@ class NetworkMessageBuilder {
107
136
  return this;
108
137
  }
109
138
 
110
- async #buildValidatorConnectionRequestPayload() {
111
- const issuer = this.#issuerAddress
112
- if (!isAddressValid(issuer, this.#config.addressPrefix)) {
113
- throw new Error('Issuer address must be a valid TRAC address');
114
- }
115
-
116
- if (this.#issuerAddress !== this.#wallet.address) {
117
- throw new Error('Issuer address must be the signer address');
118
- }
119
-
120
- const nonce = PeerWallet.generateNonce();
121
- const tsBuf = timestampToBuffer(this.#timestamp);
122
- const idBuf = idToBuffer(this.#id);
123
- const message = createMessage(
124
- this.#type,
125
- idBuf,
126
- tsBuf,
127
- addressToBuffer(issuer, this.#config.addressPrefix),
128
- nonce,
129
- encodeCapabilities(this.#capabilities),
130
- );
131
- const hash = await PeerWallet.blake3(message);
132
- const signature = this.#wallet.sign(hash);
133
-
134
- this.#payloadKey = 'validator_connection_request';
135
- this.#body = {
136
- issuer_address: issuer,
137
- nonce,
138
- signature
139
- };
140
- }
141
-
142
- async #buildValidatorConnectionResponsePayload() {
143
- const issuer = this.#issuerAddress
144
- if (!isAddressValid(issuer, this.#config.addressPrefix)) {
145
- throw new Error('Issuer address must be a valid TRAC address');
146
- }
147
-
148
- if (this.#issuerAddress === this.#wallet.address) {
149
- throw new Error('Issuer address must be the different than the signer address');
150
- }
151
-
152
- if (this.#resultCode === null || this.#resultCode === undefined) {
153
- throw new Error('Result code must be set before building validator connection response');
154
- }
155
-
156
- const nonce = PeerWallet.generateNonce();
157
- const tsBuf = timestampToBuffer(this.#timestamp);
158
- const idBuf = idToBuffer(this.#id);
159
- const message = createMessage(
160
- this.#type,
161
- idBuf,
162
- tsBuf,
163
- addressToBuffer(issuer, this.#config.addressPrefix),
164
- nonce,
165
- safeWriteUInt32BE(this.#resultCode, 0),
166
- encodeCapabilities(this.#capabilities),
167
- );
168
- const hash = await PeerWallet.blake3(message);
169
- const signature = this.#wallet.sign(hash);
170
-
171
- this.#payloadKey = 'validator_connection_response';
172
- this.#body = {
173
- issuer_address: issuer,
174
- nonce,
175
- signature,
176
- result: this.#resultCode
177
- };
178
- }
179
-
180
139
  async #buildLivenessRequestPayload() {
181
140
  const nonce = PeerWallet.generateNonce();
182
141
  const tsBuf = timestampToBuffer(this.#timestamp);
@@ -258,21 +217,50 @@ class NetworkMessageBuilder {
258
217
  const nonce = PeerWallet.generateNonce();
259
218
  const tsBuf = timestampToBuffer(this.#timestamp);
260
219
  const idBuf = idToBuffer(this.#id);
220
+ const proof = b4a.isBuffer(this.#proof) ? this.#proof : b4a.alloc(0);
221
+ const hasProof = proof.length > 0;
222
+ const timestamp = Number.isSafeInteger(this.#timestamp_ledger) ? this.#timestamp_ledger : 0;
223
+ const hasTimestamp = timestamp > 0;
224
+
225
+ if (this.#resultCode === ResultCode.OK) {
226
+ if (!hasProof || !hasTimestamp) {
227
+ throw new Error('Result code OK requires non-empty proof and timestamp > 0.');
228
+ }
229
+ } else if (this.#resultCode === ResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE) {
230
+ if (hasProof) {
231
+ throw new Error('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires empty proof.');
232
+ }
233
+ if (!hasTimestamp) {
234
+ throw new Error('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires timestamp > 0.');
235
+ }
236
+ } else {
237
+ if (hasProof) {
238
+ throw new Error('Non-OK result code requires empty proof.');
239
+ }
240
+ if (timestamp !== 0) {
241
+ throw new Error('Non-OK result code requires timestamp to be 0, except TX_ACCEPTED_PROOF_UNAVAILABLE.');
242
+ }
243
+ }
244
+
261
245
  const message = createMessage(
262
246
  this.#type,
263
247
  idBuf,
264
248
  tsBuf,
265
249
  nonce,
250
+ proof,
251
+ timestampToBuffer(timestamp),
266
252
  safeWriteUInt32BE(this.#resultCode, 0),
267
253
  encodeCapabilities(this.#capabilities),
268
254
  );
255
+
269
256
  const hash = await PeerWallet.blake3(message);
270
257
  const signature = this.#wallet.sign(hash);
271
-
272
258
  this.#payloadKey = 'broadcast_transaction_response';
273
259
  this.#body = {
274
260
  nonce,
275
261
  signature,
262
+ proof,
263
+ timestamp: timestamp,
276
264
  result: this.#resultCode
277
265
  };
278
266
  }
@@ -281,14 +269,6 @@ class NetworkMessageBuilder {
281
269
  this.#setHeader();
282
270
 
283
271
  switch (this.#type) {
284
- case NetworkOperationType.VALIDATOR_CONNECTION_REQUEST: {
285
- await this.#buildValidatorConnectionRequestPayload();
286
- break;
287
- }
288
- case NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE: {
289
- await this.#buildValidatorConnectionResponsePayload();
290
- break;
291
- }
292
272
  case NetworkOperationType.LIVENESS_REQUEST: {
293
273
  await this.#buildLivenessRequestPayload();
294
274
  break;
@@ -1,4 +1,4 @@
1
- import { NetworkOperationType } from '../../../utils/constants.js';
1
+ import {NetworkOperationType} from '../../../utils/constants.js';
2
2
 
3
3
  /**
4
4
  * Director for v1 internal network protocol messages.
@@ -13,47 +13,6 @@ class NetworkMessageDirector {
13
13
  this.#builder = builderInstance;
14
14
  }
15
15
 
16
- /**
17
- * Build a validator connection request message.
18
- * @param {string} id
19
- * @param {string} issuerAddress
20
- * @param {string[]} capabilities
21
- * @returns {Promise<object>}
22
- */
23
- async buildValidatorConnectionRequest(id, issuerAddress, capabilities) {
24
- await this.#builder
25
- .setType(NetworkOperationType.VALIDATOR_CONNECTION_REQUEST)
26
- .setId(id)
27
- .setTimestamp()
28
- .setIssuerAddress(issuerAddress)
29
- .setCapabilities(capabilities)
30
- .buildPayload()
31
-
32
-
33
- return this.#builder.getResult();
34
- }
35
-
36
- /**
37
- * Build a validator connection response message.
38
- * @param {string} id
39
- * @param {string} issuerAddress
40
- * @param {string[]} capabilities
41
- * @param {number} statusCode
42
- * @returns {Promise<object>}
43
- */
44
- async buildValidatorConnectionResponse(id, issuerAddress, capabilities, statusCode) {
45
- await this.#builder
46
- .setType(NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE)
47
- .setId(id)
48
- .setTimestamp()
49
- .setIssuerAddress(issuerAddress)
50
- .setCapabilities(capabilities)
51
- .setResultCode(statusCode)
52
- .buildPayload()
53
-
54
- return this.#builder.getResult();
55
- }
56
-
57
16
  /**
58
17
  * Build a liveness request message.
59
18
  * @param {string} id
@@ -61,12 +20,11 @@ class NetworkMessageDirector {
61
20
  * @param {string[]} capabilities
62
21
  * @returns {Promise<object>}
63
22
  */
64
- async buildLivenessRequest(id, data, capabilities) {
23
+ async buildLivenessRequest(id, capabilities) {
65
24
  await this.#builder
66
25
  .setType(NetworkOperationType.LIVENESS_REQUEST)
67
26
  .setId(id)
68
27
  .setTimestamp()
69
- .setData(data)
70
28
  .setCapabilities(capabilities)
71
29
  .buildPayload();
72
30
 
@@ -76,17 +34,15 @@ class NetworkMessageDirector {
76
34
  /**
77
35
  * Build a liveness response message.
78
36
  * @param {string} id
79
- * @param {Buffer} data
80
37
  * @param {string[]} capabilities
81
38
  * @param {number} statusCode
82
39
  * @returns {Promise<object>}
83
40
  */
84
- async buildLivenessResponse(id, data, capabilities, statusCode) {
41
+ async buildLivenessResponse(id, capabilities, statusCode) {
85
42
  await this.#builder
86
43
  .setType(NetworkOperationType.LIVENESS_RESPONSE)
87
44
  .setId(id)
88
45
  .setTimestamp()
89
- .setData(data)
90
46
  .setCapabilities(capabilities)
91
47
  .setResultCode(statusCode)
92
48
  .buildPayload();
@@ -115,18 +71,28 @@ class NetworkMessageDirector {
115
71
 
116
72
  /**
117
73
  * Build a broadcast transaction response message.
74
+ *
75
+ * Allowed payload variants:
76
+ * 1) resultCode === OK - proof must be non-empty and timestamp must be > 0.
77
+ * 2) resultCode === TX_ACCEPTED_PROOF_UNAVAILABLE - proof must be empty and timestamp must be > 0.
78
+ * 3) resultCode !== OK and resultCode !== TX_ACCEPTED_PROOF_UNAVAILABLE - proof must be empty and timestamp must be 0.
79
+ *
118
80
  * @param {string} id
119
81
  * @param {string[]} capabilities
120
- * @param {number} statusCode
82
+ * @param {number} resultCode
83
+ * @param {Buffer|null|undefined} proof
84
+ * @param {number|Date|null|undefined} timestamp - When transaction has been appended by the validator
121
85
  * @returns {Promise<object>}
122
86
  */
123
- async buildBroadcastTransactionResponse(id, capabilities, statusCode) {
87
+ async buildBroadcastTransactionResponse(id, capabilities, resultCode, proof = null, timestamp = null) {
124
88
  await this.#builder
125
89
  .setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
126
90
  .setId(id)
127
91
  .setTimestamp()
128
92
  .setCapabilities(capabilities)
129
- .setResultCode(statusCode)
93
+ .setProof(proof)
94
+ .setTimestampLedger(timestamp)
95
+ .setResultCode(resultCode)
130
96
  .buildPayload();
131
97
 
132
98
  return this.#builder.getResult();
@@ -42,14 +42,6 @@ class Scheduler {
42
42
  return this.#defaultInterval;
43
43
  }
44
44
 
45
- get timer() {
46
- return this.#timer;
47
- }
48
-
49
- get currentWorkerRun() {
50
- return this.#currentWorkerRun;
51
- }
52
-
53
45
  static #validateDelay(delayMs, scope = 'delayMs') {
54
46
  const ms = Number(delayMs);
55
47
  if (!Number.isFinite(ms) || ms < 0) {
@@ -1,8 +1,10 @@
1
1
  import { OperationType as ApplyOperationType } from './protobuf/applyOperations.cjs';
2
- import { MessageType as NetworkMessageType, ResultCode as NetworkResultCode } from './protobuf/network.cjs';
2
+ import networkV1Generated from './protobuf/networkV1.generated.cjs';
3
3
  import b4a from 'b4a'
4
4
  // TODO: We are going to have a lot of contstants. It would be good, to separate them into different files.
5
5
 
6
+ const { MessageType: NetworkMessageType, ResultCode: NetworkResultCode } = networkV1Generated.network.v1;
7
+
6
8
  //ATTENTION - THIS IS USED IN THE APPLY FUNCTION!
7
9
  export const EntryType = Object.freeze({
8
10
  ADMIN: 'admin',
@@ -34,8 +36,6 @@ export const OperationType = Object.freeze({
34
36
  });
35
37
 
36
38
  export const NetworkOperationType = Object.freeze({
37
- VALIDATOR_CONNECTION_REQUEST: NetworkMessageType.MESSAGE_TYPE_VALIDATOR_CONNECTION_REQUEST,
38
- VALIDATOR_CONNECTION_RESPONSE: NetworkMessageType.MESSAGE_TYPE_VALIDATOR_CONNECTION_RESPONSE,
39
39
  LIVENESS_REQUEST: NetworkMessageType.MESSAGE_TYPE_LIVENESS_REQUEST,
40
40
  LIVENESS_RESPONSE: NetworkMessageType.MESSAGE_TYPE_LIVENESS_RESPONSE,
41
41
  BROADCAST_TRANSACTION_REQUEST: NetworkMessageType.MESSAGE_TYPE_BROADCAST_TRANSACTION_REQUEST,
@@ -43,12 +43,68 @@ export const NetworkOperationType = Object.freeze({
43
43
  });
44
44
 
45
45
  export const ResultCode = Object.freeze({
46
+ UNSPECIFIED: NetworkResultCode.RESULT_CODE_UNSPECIFIED,
46
47
  OK: NetworkResultCode.RESULT_CODE_OK,
47
48
  INVALID_PAYLOAD: NetworkResultCode.RESULT_CODE_INVALID_PAYLOAD,
48
- UNSUPPORTED_VERSION: NetworkResultCode.RESULT_CODE_UNSUPPORTED_VERSION,
49
49
  RATE_LIMITED: NetworkResultCode.RESULT_CODE_RATE_LIMITED,
50
- TIMEOUT: NetworkResultCode.RESULT_CODE_TIMEOUT,
51
50
  SIGNATURE_INVALID: NetworkResultCode.RESULT_CODE_SIGNATURE_INVALID,
51
+ UNEXPECTED_ERROR: NetworkResultCode.RESULT_CODE_UNEXPECTED_ERROR,
52
+ TIMEOUT: NetworkResultCode.RESULT_CODE_TIMEOUT,
53
+ NODE_HAS_NO_WRITE_ACCESS: NetworkResultCode.RESULT_CODE_NODE_HAS_NO_WRITE_ACCESS,
54
+ TX_ACCEPTED_PROOF_UNAVAILABLE: NetworkResultCode.RESULT_CODE_TX_ACCEPTED_PROOF_UNAVAILABLE,
55
+ NODE_OVERLOADED: NetworkResultCode.RESULT_CODE_NODE_OVERLOADED,
56
+ TX_ALREADY_PENDING: NetworkResultCode.RESULT_CODE_TX_ALREADY_PENDING,
57
+ OPERATION_TYPE_UNKNOWN: NetworkResultCode.RESULT_CODE_OPERATION_TYPE_UNKNOWN,
58
+ SCHEMA_VALIDATION_FAILED: NetworkResultCode.RESULT_CODE_SCHEMA_VALIDATION_FAILED,
59
+ REQUESTER_ADDRESS_INVALID: NetworkResultCode.RESULT_CODE_REQUESTER_ADDRESS_INVALID,
60
+ REQUESTER_PUBLIC_KEY_INVALID: NetworkResultCode.RESULT_CODE_REQUESTER_PUBLIC_KEY_INVALID,
61
+ TX_HASH_MISMATCH: NetworkResultCode.RESULT_CODE_TX_HASH_MISMATCH,
62
+ TX_SIGNATURE_INVALID: NetworkResultCode.RESULT_CODE_TX_SIGNATURE_INVALID,
63
+ TX_EXPIRED: NetworkResultCode.RESULT_CODE_TX_EXPIRED,
64
+ TX_ALREADY_EXISTS: NetworkResultCode.RESULT_CODE_TX_ALREADY_EXISTS,
65
+ OPERATION_ALREADY_COMPLETED: NetworkResultCode.RESULT_CODE_OPERATION_ALREADY_COMPLETED,
66
+ REQUESTER_NOT_FOUND: NetworkResultCode.RESULT_CODE_REQUESTER_NOT_FOUND,
67
+ INSUFFICIENT_FEE_BALANCE: NetworkResultCode.RESULT_CODE_INSUFFICIENT_FEE_BALANCE,
68
+ EXTERNAL_BOOTSTRAP_EQUALS_MSB_BOOTSTRAP: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_EQUALS_MSB_BOOTSTRAP,
69
+ SELF_VALIDATION_FORBIDDEN: NetworkResultCode.RESULT_CODE_SELF_VALIDATION_FORBIDDEN,
70
+ ROLE_NODE_ENTRY_NOT_FOUND: NetworkResultCode.RESULT_CODE_ROLE_NODE_ENTRY_NOT_FOUND,
71
+ ROLE_NODE_ALREADY_WRITER: NetworkResultCode.RESULT_CODE_ROLE_NODE_ALREADY_WRITER,
72
+ ROLE_NODE_NOT_WHITELISTED: NetworkResultCode.RESULT_CODE_ROLE_NODE_NOT_WHITELISTED,
73
+ ROLE_NODE_NOT_WRITER: NetworkResultCode.RESULT_CODE_ROLE_NODE_NOT_WRITER,
74
+ ROLE_NODE_IS_INDEXER: NetworkResultCode.RESULT_CODE_ROLE_NODE_IS_INDEXER,
75
+ ROLE_ADMIN_ENTRY_MISSING: NetworkResultCode.RESULT_CODE_ROLE_ADMIN_ENTRY_MISSING,
76
+ ROLE_INVALID_RECOVERY_CASE: NetworkResultCode.RESULT_CODE_ROLE_INVALID_RECOVERY_CASE,
77
+ ROLE_UNKNOWN_OPERATION: NetworkResultCode.RESULT_CODE_ROLE_UNKNOWN_OPERATION,
78
+ ROLE_INVALID_WRITER_KEY: NetworkResultCode.RESULT_CODE_ROLE_INVALID_WRITER_KEY,
79
+ ROLE_INSUFFICIENT_FEE_BALANCE: NetworkResultCode.RESULT_CODE_ROLE_INSUFFICIENT_FEE_BALANCE,
80
+ MSB_BOOTSTRAP_MISMATCH: NetworkResultCode.RESULT_CODE_MSB_BOOTSTRAP_MISMATCH,
81
+ EXTERNAL_BOOTSTRAP_NOT_DEPLOYED: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_NOT_DEPLOYED,
82
+ EXTERNAL_BOOTSTRAP_TX_MISSING: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_TX_MISSING,
83
+ EXTERNAL_BOOTSTRAP_MISMATCH: NetworkResultCode.RESULT_CODE_EXTERNAL_BOOTSTRAP_MISMATCH,
84
+ BOOTSTRAP_ALREADY_EXISTS: NetworkResultCode.RESULT_CODE_BOOTSTRAP_ALREADY_EXISTS,
85
+ TRANSFER_RECIPIENT_ADDRESS_INVALID: NetworkResultCode.RESULT_CODE_TRANSFER_RECIPIENT_ADDRESS_INVALID,
86
+ TRANSFER_RECIPIENT_PUBLIC_KEY_INVALID: NetworkResultCode.RESULT_CODE_TRANSFER_RECIPIENT_PUBLIC_KEY_INVALID,
87
+ TRANSFER_AMOUNT_TOO_LARGE: NetworkResultCode.RESULT_CODE_TRANSFER_AMOUNT_TOO_LARGE,
88
+ TRANSFER_SENDER_NOT_FOUND: NetworkResultCode.RESULT_CODE_TRANSFER_SENDER_NOT_FOUND,
89
+ TRANSFER_INSUFFICIENT_BALANCE: NetworkResultCode.RESULT_CODE_TRANSFER_INSUFFICIENT_BALANCE,
90
+ TRANSFER_RECIPIENT_BALANCE_OVERFLOW: NetworkResultCode.RESULT_CODE_TRANSFER_RECIPIENT_BALANCE_OVERFLOW,
91
+ TX_HASH_INVALID_FORMAT: NetworkResultCode.RESULT_CODE_TX_HASH_INVALID_FORMAT,
92
+ INTERNAL_ENQUEUE_VALIDATION_FAILED: NetworkResultCode.RESULT_CODE_INTERNAL_ENQUEUE_VALIDATION_FAILED,
93
+ TX_COMMITTED_RECEIPT_MISSING: NetworkResultCode.RESULT_CODE_TX_COMMITTED_RECEIPT_MISSING,
94
+ VALIDATOR_RESPONSE_TX_TYPE_INVALID: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_TX_TYPE_INVALID,
95
+ VALIDATOR_RESPONSE_TX_TYPE_UNKNOWN: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_TX_TYPE_UNKNOWN,
96
+ VALIDATOR_RESPONSE_TX_TYPE_UNSUPPORTED: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_TX_TYPE_UNSUPPORTED,
97
+ VALIDATOR_RESPONSE_SCHEMA_INVALID: NetworkResultCode.RESULT_CODE_VALIDATOR_RESPONSE_SCHEMA_INVALID,
98
+ PENDING_REQUEST_MISSING_TX_DATA: NetworkResultCode.RESULT_CODE_PENDING_REQUEST_MISSING_TX_DATA,
99
+ PROOF_PAYLOAD_MISMATCH: NetworkResultCode.RESULT_CODE_PROOF_PAYLOAD_MISMATCH,
100
+ VALIDATOR_WRITER_KEY_NOT_REGISTERED: NetworkResultCode.RESULT_CODE_VALIDATOR_WRITER_KEY_NOT_REGISTERED,
101
+ VALIDATOR_ADDRESS_MISMATCH: NetworkResultCode.RESULT_CODE_VALIDATOR_ADDRESS_MISMATCH,
102
+ VALIDATOR_NODE_ENTRY_NOT_FOUND: NetworkResultCode.RESULT_CODE_VALIDATOR_NODE_ENTRY_NOT_FOUND,
103
+ VALIDATOR_NODE_NOT_WRITER: NetworkResultCode.RESULT_CODE_VALIDATOR_NODE_NOT_WRITER,
104
+ VALIDATOR_WRITER_KEY_MISMATCH: NetworkResultCode.RESULT_CODE_VALIDATOR_WRITER_KEY_MISMATCH,
105
+ VALIDATOR_TX_OBJECT_INVALID: NetworkResultCode.RESULT_CODE_VALIDATOR_TX_OBJECT_INVALID,
106
+ VALIDATOR_VA_MISSING: NetworkResultCode.RESULT_CODE_VALIDATOR_VA_MISSING,
107
+ TX_INVALID_PAYLOAD: NetworkResultCode.RESULT_CODE_TX_INVALID_PAYLOAD
52
108
  });
53
109
 
54
110
  // Role managment constants
@@ -61,6 +117,7 @@ export const EventType = Object.freeze({
61
117
  WARNING: 'warning',
62
118
  VALIDATOR_CONNECTION_READY: 'validator-connection-ready',
63
119
  VALIDATOR_CONNECTION_TIMEOUT: 'validator-connection-timeout',
120
+ VALIDATOR_HEALTH_CHECK: 'validator-health-check',
64
121
  });
65
122
 
66
123
  // Role managment constants
@@ -109,6 +166,13 @@ export const BOOTSTRAP_HEXSTRING_LENGTH = 64;
109
166
  // Pool constants
110
167
  export const BATCH_SIZE = 10;
111
168
 
169
+ // Operation handler constants
170
+ export const MAX_PARTIAL_TX_PAYLOAD_BYTE_SIZE = 3072;
171
+ export const V1_PROTOCOL_PAYLOAD_MAX_SIZE = 4096;
172
+
173
+ // Transaction Commit Service
174
+ export const TRANSACTION_COMMIT_SERVICE_BUFFER_SIZE = 1500;
175
+
112
176
  // Network message constants
113
177
  export const NETWORK_MESSAGE_TYPES = Object.freeze({
114
178
  GET: {
@@ -123,3 +187,5 @@ export const NETWORK_MESSAGE_TYPES = Object.freeze({
123
187
  NODE: 'nodeResponse'
124
188
  },
125
189
  });
190
+
191
+ export const NETWORK_CAPABILITIES = Object.freeze(["protocols:v1:legacy"]);
@@ -0,0 +1,40 @@
1
+ import b4a from 'b4a';
2
+ import _ from 'lodash';
3
+
4
+ export const isDeepEqualApplyPayload = (left, right) => {
5
+ if (left === right) return true;
6
+
7
+ const leftIsBuffer = b4a.isBuffer(left);
8
+ const rightIsBuffer = b4a.isBuffer(right);
9
+
10
+ if (leftIsBuffer || rightIsBuffer) {
11
+ return leftIsBuffer && rightIsBuffer && b4a.equals(left, right);
12
+ }
13
+
14
+ const leftIsArray = Array.isArray(left);
15
+ const rightIsArray = Array.isArray(right);
16
+
17
+ if (leftIsArray || rightIsArray) {
18
+ if (!leftIsArray || !rightIsArray) return false;
19
+ if (left.length !== right.length) return false;
20
+
21
+ for (let i = 0; i < left.length; i++) {
22
+ if (!isDeepEqualApplyPayload(left[i], right[i])) return false;
23
+ }
24
+ return true;
25
+ }
26
+
27
+ if (!_.isObject(left) || !_.isObject(right)) return false;
28
+
29
+ const leftKeys = Object.keys(left);
30
+ const rightKeys = Object.keys(right);
31
+
32
+ if (leftKeys.length !== rightKeys.length) return false;
33
+
34
+ for (const key of leftKeys) {
35
+ if (!Object.prototype.hasOwnProperty.call(right, key)) return false;
36
+ if (!isDeepEqualApplyPayload(left[key], right[key])) return false;
37
+ }
38
+
39
+ return true;
40
+ };