trac-msb 0.2.8 → 0.2.10

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 (151) hide show
  1. package/.github/workflows/acceptance-tests.yml +7 -11
  2. package/.github/workflows/lint-pr-title.yml +26 -0
  3. package/.github/workflows/unit-tests.yml +2 -8
  4. package/CODE_OF_CONDUCT.md +128 -0
  5. package/README.md +33 -18
  6. package/docker-compose.yml +1 -0
  7. package/docs/trac_network_http_api.openapi.yaml +889 -0
  8. package/msb.mjs +5 -22
  9. package/package.json +14 -10
  10. package/proto/network.proto +74 -0
  11. package/rpc/create_server.js +2 -2
  12. package/rpc/handlers.js +165 -92
  13. package/rpc/routes/v1.js +3 -1
  14. package/rpc/rpc_server.js +4 -4
  15. package/rpc/rpc_services.js +62 -25
  16. package/rpc/utils/helpers.js +83 -52
  17. package/src/config/args.js +46 -0
  18. package/src/config/config.js +78 -5
  19. package/src/config/env.js +70 -3
  20. package/src/core/network/Network.js +34 -70
  21. package/src/core/network/identity/NetworkWalletFactory.js +2 -2
  22. package/src/core/network/protocols/LegacyProtocol.js +67 -0
  23. package/src/core/network/protocols/NetworkMessages.js +48 -0
  24. package/src/core/network/protocols/ProtocolInterface.js +31 -0
  25. package/src/core/network/protocols/ProtocolSession.js +59 -0
  26. package/src/core/network/protocols/V1Protocol.js +64 -0
  27. package/src/core/network/protocols/legacy/NetworkMessageRouter.js +84 -0
  28. package/src/core/network/protocols/legacy/handlers/GetRequestHandler.js +53 -0
  29. package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
  30. package/src/core/network/{messaging → protocols/legacy}/validators/base/BaseResponse.js +2 -3
  31. package/src/core/network/protocols/shared/handlers/RoleOperationHandler.js +88 -0
  32. package/src/core/network/protocols/shared/handlers/SubnetworkOperationHandler.js +93 -0
  33. package/src/core/network/{messaging → protocols/shared}/handlers/TransferOperationHandler.js +17 -16
  34. package/src/core/network/{messaging → protocols/shared}/handlers/base/BaseOperationHandler.js +10 -15
  35. package/src/core/network/{messaging → protocols/shared}/validators/PartialBootstrapDeployment.js +2 -2
  36. package/src/core/network/{messaging → protocols/shared}/validators/PartialRoleAccess.js +5 -5
  37. package/src/core/network/{messaging → protocols/shared}/validators/PartialTransaction.js +4 -4
  38. package/src/core/network/{messaging → protocols/shared}/validators/PartialTransfer.js +4 -4
  39. package/src/core/network/{messaging → protocols/shared}/validators/base/PartialOperation.js +14 -12
  40. package/src/core/network/protocols/v1/NetworkMessageRouter.js +15 -0
  41. package/src/core/network/services/ConnectionManager.js +5 -5
  42. package/src/core/network/services/MessageOrchestrator.js +2 -2
  43. package/src/core/network/services/TransactionPoolService.js +5 -6
  44. package/src/core/network/services/TransactionRateLimiterService.js +12 -13
  45. package/src/core/network/services/ValidatorObserverService.js +5 -6
  46. package/src/core/state/State.js +3 -5
  47. package/src/index.js +156 -181
  48. package/src/messages/network/v1/NetworkMessageBuilder.js +325 -0
  49. package/src/messages/network/v1/NetworkMessageDirector.js +137 -0
  50. package/src/messages/network/v1/networkMessageFactory.js +12 -0
  51. package/src/messages/state/ApplyStateMessageBuilder.js +661 -0
  52. package/src/messages/state/ApplyStateMessageDirector.js +516 -0
  53. package/src/messages/state/applyStateMessageFactory.js +12 -0
  54. package/src/utils/buffer.js +53 -1
  55. package/src/utils/check.js +1 -1
  56. package/src/utils/cli.js +0 -8
  57. package/src/utils/constants.js +33 -30
  58. package/src/utils/fileUtils.js +13 -0
  59. package/src/utils/normalizers.js +84 -2
  60. package/src/utils/protobuf/network.cjs +840 -0
  61. package/src/utils/protobuf/operationHelpers.js +10 -0
  62. package/src/utils/type.js +26 -0
  63. package/tests/acceptance/v1/balance/balance.test.mjs +1 -2
  64. package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +26 -30
  65. package/tests/acceptance/v1/health/health.test.mjs +33 -0
  66. package/tests/acceptance/v1/rpc.test.mjs +4 -3
  67. package/tests/acceptance/v1/tx/tx.test.mjs +27 -16
  68. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +26 -12
  69. package/tests/fixtures/check.fixtures.js +33 -32
  70. package/tests/fixtures/networkV1.fixtures.js +85 -0
  71. package/tests/fixtures/protobuf.fixtures.js +109 -25
  72. package/tests/helpers/StateNetworkFactory.js +2 -2
  73. package/tests/helpers/address.js +6 -0
  74. package/tests/helpers/autobaseTestHelpers.js +2 -1
  75. package/tests/helpers/config.js +2 -1
  76. package/tests/helpers/setupApplyTests.js +59 -56
  77. package/tests/unit/messages/messages.test.js +12 -0
  78. package/tests/unit/messages/network/NetworkMessageBuilder.test.js +276 -0
  79. package/tests/unit/messages/network/NetworkMessageDirector.test.js +201 -0
  80. package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +521 -0
  81. package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +233 -0
  82. package/tests/unit/network/ConnectionManager.test.js +6 -5
  83. package/tests/unit/network/networkModule.test.js +3 -2
  84. package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +10 -6
  85. package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +9 -6
  86. package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +10 -7
  87. package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +18 -21
  88. package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +53 -38
  89. package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +46 -35
  90. package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +13 -16
  91. package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +17 -11
  92. package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +11 -12
  93. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +9 -7
  94. package/tests/unit/state/apply/common/commonScenarioHelper.js +15 -14
  95. package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +9 -4
  96. package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +17 -11
  97. package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +19 -14
  98. package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +37 -29
  99. package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +9 -7
  100. package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +11 -9
  101. package/tests/unit/unit.test.js +1 -1
  102. package/tests/unit/utils/buffer/buffer.test.js +62 -1
  103. package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +4 -3
  104. package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +3 -2
  105. package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +3 -2
  106. package/tests/unit/utils/normalizers/normalizers.test.js +469 -0
  107. package/tests/unit/utils/protobuf/operationHelpers.test.js +120 -2
  108. package/tests/unit/utils/type/type.test.js +25 -0
  109. package/tests/unit/utils/utils.test.js +1 -0
  110. package/docs/networking-dualstack-plan.md +0 -75
  111. package/docs/networking-layer-redesign.md +0 -155
  112. package/src/core/network/messaging/NetworkMessages.js +0 -64
  113. package/src/core/network/messaging/handlers/GetRequestHandler.js +0 -113
  114. package/src/core/network/messaging/handlers/ResponseHandler.js +0 -107
  115. package/src/core/network/messaging/handlers/RoleOperationHandler.js +0 -114
  116. package/src/core/network/messaging/handlers/SubnetworkOperationHandler.js +0 -149
  117. package/src/core/network/messaging/routes/NetworkMessageRouter.js +0 -98
  118. package/src/core/network/messaging/validators/AdminResponse.js +0 -58
  119. package/src/core/network/messaging/validators/CustomNodeResponse.js +0 -46
  120. package/src/messages/base/StateBuilder.js +0 -25
  121. package/src/messages/completeStateMessages/CompleteStateMessageBuilder.js +0 -425
  122. package/src/messages/completeStateMessages/CompleteStateMessageDirector.js +0 -252
  123. package/src/messages/completeStateMessages/CompleteStateMessageOperations.js +0 -296
  124. package/src/messages/partialStateMessages/PartialStateMessageBuilder.js +0 -272
  125. package/src/messages/partialStateMessages/PartialStateMessageDirector.js +0 -137
  126. package/src/messages/partialStateMessages/PartialStateMessageOperations.js +0 -138
  127. package/tests/integration/apply/addAdmin/addAdminBasic.test.js +0 -69
  128. package/tests/integration/apply/addAdmin/addAdminRecovery.test.js +0 -126
  129. package/tests/integration/apply/addIndexer.test.js +0 -239
  130. package/tests/integration/apply/addWhitelist.test.js +0 -53
  131. package/tests/integration/apply/addWriter.test.js +0 -245
  132. package/tests/integration/apply/apply.test.js +0 -19
  133. package/tests/integration/apply/banValidator.test.js +0 -116
  134. package/tests/integration/apply/postTx/invalidSubValues.test.js +0 -103
  135. package/tests/integration/apply/postTx/postTx.test.js +0 -196
  136. package/tests/integration/apply/removeIndexer.test.js +0 -132
  137. package/tests/integration/apply/removeWriter.test.js +0 -168
  138. package/tests/integration/apply/transfer.test.js +0 -83
  139. package/tests/integration/integration.test.js +0 -9
  140. package/tests/unit/messageOperations/assembleAddIndexerMessage.test.js +0 -21
  141. package/tests/unit/messageOperations/assembleAddWriterMessage.test.js +0 -17
  142. package/tests/unit/messageOperations/assembleAdminMessage.test.js +0 -68
  143. package/tests/unit/messageOperations/assembleBanWriterMessage.test.js +0 -17
  144. package/tests/unit/messageOperations/assemblePostTransaction.test.js +0 -424
  145. package/tests/unit/messageOperations/assembleRemoveIndexerMessage.test.js +0 -19
  146. package/tests/unit/messageOperations/assembleRemoveWriterMessage.test.js +0 -17
  147. package/tests/unit/messageOperations/assembleWhitelistMessages.test.js +0 -59
  148. package/tests/unit/messageOperations/commonsStateMessageOperationsTest.js +0 -278
  149. package/tests/unit/messageOperations/stateMessageOperations.test.js +0 -19
  150. /package/src/core/network/{messaging → protocols/legacy}/validators/ValidatorResponse.js +0 -0
  151. /package/src/utils/{operations.js → applyOperations.js} +0 -0
@@ -3,16 +3,13 @@ import {generateMnemonic, mnemonicToSeed} from 'bip39-mnemonic';
3
3
  import b4a from 'b4a'
4
4
  import PeerWallet from "trac-wallet"
5
5
  import path from 'path';
6
- import CompleteStateMessageOperations from '../../src/messages/completeStateMessages/CompleteStateMessageOperations.js'
7
- import PartialStateMessageOperations from '../../src/messages/partialStateMessages/PartialStateMessageOperations.js';
8
6
  import {MainSettlementBus} from '../../src/index.js'
9
7
  import { createConfig, ENV } from '../../src/config/env.js'
10
8
  import fileUtils from '../../src/utils/fileUtils.js'
11
9
  import {EntryType} from '../../src/utils/constants.js';
12
10
  import {sleep} from '../../src/utils/helpers.js'
13
11
  import {formatIndexersEntry} from '../../src/utils/helpers.js';
14
- import CompleteStateMessageBuilder from '../../src/messages/completeStateMessages/CompleteStateMessageBuilder.js'
15
- import CompleteStateMessageDirector from '../../src/messages/completeStateMessages/CompleteStateMessageDirector.js'
12
+ import { applyStateMessageFactory } from "../../src/messages/state/applyStateMessageFactory.js";
16
13
  import { safeEncodeApplyOperation } from "../../src/utils/protobuf/operationHelpers.js"
17
14
  import { $TNK } from '../../src/core/state/utils/balance.js';
18
15
  import { EventType } from '../../src/utils/constants.js';
@@ -69,14 +66,13 @@ export const tick = () => new Promise(resolve => setImmediate(resolve));
69
66
 
70
67
  export async function fundPeer(admin, toFund, amount) {
71
68
  const txValidity = await admin.msb.state.getIndexerSequenceState()
72
- const director = new CompleteStateMessageDirector();
73
- director.builder = new CompleteStateMessageBuilder(admin.wallet, admin.config);
74
- const payload = await director.buildBalanceInitializationMessage(
75
- admin.wallet.address,
76
- toFund.wallet.address,
77
- amount,
78
- txValidity
79
- );
69
+ const payload = await applyStateMessageFactory(admin.wallet, admin.config)
70
+ .buildCompleteBalanceInitializationMessage(
71
+ admin.wallet.address,
72
+ toFund.wallet.address,
73
+ amount,
74
+ txValidity
75
+ );
80
76
 
81
77
  await admin.msb.state.append(safeEncodeApplyOperation(payload));
82
78
  await tick()
@@ -86,11 +82,10 @@ export async function fundPeer(admin, toFund, amount) {
86
82
  }
87
83
 
88
84
  export async function initMsbPeer(peerName, peerKeyPair, temporaryDirectory, options = {}) {
89
- const peer = await initDirectoryStructure(peerName, peerKeyPair, temporaryDirectory);
90
- peer.options = options
91
- peer.options.storesDirectory = peer.storesDirectory;
92
- peer.options.storeName = peer.storeName;
93
- peer.config = createConfig(ENV.DEVELOPMENT, peer.options)
85
+ const config = createConfig(ENV.DEVELOPMENT, { ...options, storesDirectory: `${temporaryDirectory}/${peerName}` })
86
+ const peer = await initDirectoryStructure(peerName, peerKeyPair, config.storesFullPath);
87
+ peer.options = { ...options, storesDirectory: config.storesDirectory }
88
+ peer.config = config
94
89
  const msb = new MainSettlementBus(peer.config);
95
90
 
96
91
  peer.msb = msb;
@@ -124,10 +119,10 @@ export async function initMsbAdmin(keyPair, temporaryDirectory, options = {}) {
124
119
  export async function setupMsbAdmin(keyPair, temporaryDirectory, options = {}) {
125
120
  const admin = await initMsbAdmin(keyPair, temporaryDirectory, options);
126
121
  const txValidity = await admin.msb.state.getIndexerSequenceState();
127
- const addAdminMessage = await new CompleteStateMessageOperations(admin.wallet, admin.config)
128
- .assembleAddAdminMessage(admin.msb.state.writingKey, txValidity);
122
+ const payload = await applyStateMessageFactory(admin.wallet, admin.config)
123
+ .buildCompleteAddAdminMessage(admin.wallet.address, admin.msb.state.writingKey, txValidity);
129
124
 
130
- await admin.msb.state.append(addAdminMessage);
125
+ await admin.msb.state.append(safeEncodeApplyOperation(payload));
131
126
  await tick();
132
127
  return admin;
133
128
  }
@@ -137,22 +132,25 @@ export async function setupNodeAsWriter(admin, writerCandidate) {
137
132
  await setupWhitelist(admin, [writerCandidate.wallet.address]); // ensure if is whitelisted
138
133
 
139
134
  const validity = await admin.msb.getIndexerSequenceState()
140
- const req = await new PartialStateMessageOperations(writerCandidate.wallet, admin.config)
141
- .assembleAddWriterMessage(
135
+ const req = await applyStateMessageFactory(writerCandidate.wallet, admin.config)
136
+ .buildPartialAddWriterMessage(
137
+ writerCandidate.wallet.address,
142
138
  b4a.toString(writerCandidate.msb.state.writingKey, 'hex'),
143
- b4a.toString(validity, 'hex'));
139
+ b4a.toString(validity, 'hex'),
140
+ 'json'
141
+ );
144
142
 
145
143
  await waitWritable(admin, writerCandidate, async () => {
146
- const raw = await new CompleteStateMessageOperations(admin.wallet, admin.config)
147
- .assembleAddWriterMessage(
144
+ const payload = await applyStateMessageFactory(admin.wallet, admin.config)
145
+ .buildCompleteAddWriterMessage(
148
146
  admin.wallet.address,
149
147
  b4a.from(req.rao.tx, 'hex'),
150
148
  b4a.from(req.rao.txv, 'hex'),
151
149
  b4a.from(req.rao.iw, 'hex'),
152
150
  b4a.from(req.rao.in, 'hex'),
153
151
  b4a.from(req.rao.is, 'hex')
154
- )
155
- await admin.msb.state.append(raw)
152
+ );
153
+ await admin.msb.state.append(safeEncodeApplyOperation(payload))
156
154
  })
157
155
 
158
156
  return writerCandidate;
@@ -172,22 +170,25 @@ export async function promoteToWriter(admin, writerCandidate) {
172
170
  isIndexer: false,
173
171
  })
174
172
  const validity = await admin.msb.state.getIndexerSequenceState()
175
- const req = await new PartialStateMessageOperations(writerCandidate.wallet, writerCandidate.config)
176
- .assembleAddWriterMessage(
173
+ const req = await applyStateMessageFactory(writerCandidate.wallet, writerCandidate.config)
174
+ .buildPartialAddWriterMessage(
175
+ writerCandidate.wallet.address,
177
176
  b4a.toString(writerCandidate.msb.state.writingKey, 'hex'),
178
- b4a.toString(validity, 'hex'));
177
+ b4a.toString(validity, 'hex'),
178
+ 'json'
179
+ );
179
180
 
180
181
  await waitWritable(writerCandidate, writerCandidate, async () => {
181
- const raw = await new CompleteStateMessageOperations(admin.wallet, admin.config)
182
- .assembleAddWriterMessage(
182
+ const payload = await applyStateMessageFactory(admin.wallet, admin.config)
183
+ .buildCompleteAddWriterMessage(
183
184
  req.address,
184
185
  b4a.from(req.rao.tx, 'hex'),
185
186
  b4a.from(req.rao.txv, 'hex'),
186
187
  b4a.from(req.rao.iw, 'hex'),
187
188
  b4a.from(req.rao.in, 'hex'),
188
189
  b4a.from(req.rao.is, 'hex')
189
- )
190
- await admin.msb.state.append(raw)
190
+ );
191
+ await admin.msb.state.append(safeEncodeApplyOperation(payload))
191
192
  })
192
193
 
193
194
  return writerCandidate;
@@ -206,10 +207,10 @@ export async function setupMsbWriter(admin, peerName, peerKeyPair, temporaryDire
206
207
  export async function setupMsbIndexer(indexerCandidate, admin) {
207
208
  try {
208
209
  const validity = await admin.msb.state.getIndexerSequenceState()
209
- const req = await new CompleteStateMessageOperations(admin.wallet, admin.config)
210
- .assembleAddIndexerMessage(indexerCandidate.wallet.address, validity);
210
+ const payload = await applyStateMessageFactory(admin.wallet, admin.config)
211
+ .buildCompleteAddIndexerMessage(admin.wallet.address, indexerCandidate.wallet.address, validity);
211
212
 
212
- await admin.msb.state.append(req);
213
+ await admin.msb.state.append(safeEncodeApplyOperation(payload));
213
214
  await tick(); // wait for the request to be processed
214
215
 
215
216
  const isIndexer = async () => {
@@ -260,10 +261,10 @@ export async function setupWhitelist(admin, whitelistAddresses) {
260
261
  fileUtils.readAddressesFromWhitelistFile = async () => whitelistAddresses;
261
262
  const validity = await admin.msb.state.getIndexerSequenceState()
262
263
  for (const address of whitelistAddresses) {
263
- const msg = await new CompleteStateMessageOperations(admin.wallet, admin.config)
264
- .assembleAppendWhitelistMessages(validity, address);
264
+ const payload = await applyStateMessageFactory(admin.wallet, admin.config)
265
+ .buildCompleteAppendWhitelistMessage(admin.wallet.address, address, validity);
265
266
 
266
- await admin.msb.state.append(msg);
267
+ await admin.msb.state.append(safeEncodeApplyOperation(payload));
267
268
  await sleep(100)
268
269
  }
269
270
 
@@ -288,9 +289,7 @@ export async function removeTemporaryDirectory(temporaryDirectory) {
288
289
  export async function initDirectoryStructure(peerName, keyPair, temporaryDirectory) {
289
290
  try {
290
291
  await ensureEnvReady();
291
- const storesDirectory = temporaryDirectory + '/stores/';
292
- const storeName = peerName + '/';
293
- const corestoreDbDirectory = path.join(storesDirectory, storeName, 'db');
292
+ const corestoreDbDirectory = path.join(temporaryDirectory, 'db');
294
293
  await fsp.mkdir(corestoreDbDirectory, {recursive: true});
295
294
 
296
295
  const keypath = path.join(corestoreDbDirectory, 'keypair.json');
@@ -301,8 +300,7 @@ export async function initDirectoryStructure(peerName, keyPair, temporaryDirecto
301
300
  await wallet.ready
302
301
  await wallet.exportToFile(keypath)
303
302
  return {
304
- storesDirectory,
305
- storeName,
303
+ storesDirectory: temporaryDirectory,
306
304
  corestoreDbDirectory,
307
305
  keypath,
308
306
  }
@@ -314,15 +312,17 @@ export async function initDirectoryStructure(peerName, keyPair, temporaryDirecto
314
312
  export const deployExternalBootstrap = async (writer, externalNode) => {
315
313
  const externalBootstrap = randomBytes(32).toString('hex');
316
314
  const txValidity = await writer.msb.state.getIndexerSequenceState();
317
- const payload = await new PartialStateMessageOperations(externalNode.msb.wallet, admin.config)
318
- .assembleBootstrapDeploymentMessage(
315
+ const payload = await applyStateMessageFactory(externalNode.msb.wallet, admin.config)
316
+ .buildPartialBootstrapDeploymentMessage(
317
+ externalNode.msb.wallet.address,
319
318
  externalBootstrap,
320
319
  randomBytes(32).toString('hex'),
321
- txValidity.toString('hex')
320
+ txValidity.toString('hex'),
321
+ 'json'
322
322
  );
323
323
 
324
- const raw = await new CompleteStateMessageOperations(writer.msb.wallet, admin.config)
325
- .assembleCompleteBootstrapDeployment(
324
+ const rawPayload = await applyStateMessageFactory(writer.msb.wallet, admin.config)
325
+ .buildCompleteBootstrapDeploymentMessage(
326
326
  payload.address,
327
327
  b4a.from(payload.bdo.tx, 'hex'),
328
328
  b4a.from(payload.bdo.txv, 'hex'),
@@ -331,7 +331,7 @@ export const deployExternalBootstrap = async (writer, externalNode) => {
331
331
  b4a.from(payload.bdo.in, 'hex'),
332
332
  b4a.from(payload.bdo.is, 'hex'),
333
333
  )
334
- await writer.msb.state.base.append(raw)
334
+ await writer.msb.state.base.append(safeEncodeApplyOperation(rawPayload))
335
335
  await tick()
336
336
  await waitForHash(writer, payload.bdo.tx)
337
337
  return externalBootstrap
@@ -353,17 +353,19 @@ export const generatePostTx = async (writer, externalNode, externalContractBoots
353
353
 
354
354
  const contentHash = await PeerWallet.blake3(JSON.stringify(testObj));
355
355
  const validity = await writer.msb.state.getIndexerSequenceState()
356
- const tx = await new PartialStateMessageOperations(externalNode.wallet, admin.config)
357
- .assembleTransactionOperationMessage(
356
+ const tx = await applyStateMessageFactory(externalNode.wallet, admin.config)
357
+ .buildPartialTransactionOperationMessage(
358
+ externalNode.wallet.address,
358
359
  peerWriterKey,
359
360
  b4a.toString(validity, 'hex'),
360
361
  b4a.toString(contentHash, 'hex'),
361
362
  externalContractBootstrap,
362
- b4a.toString(writer.msb.bootstrap, 'hex')
363
+ b4a.toString(writer.msb.bootstrap, 'hex'),
364
+ 'json'
363
365
  )
364
366
 
365
- const postTx = await new CompleteStateMessageOperations(writer.wallet, admin.config)
366
- .assembleCompleteTransactionOperationMessage(
367
+ const postTxPayload = await applyStateMessageFactory(writer.wallet, admin.config)
368
+ .buildCompleteTransactionOperationMessage(
367
369
  tx.address,
368
370
  b4a.from(tx.txo.tx, 'hex'),
369
371
  b4a.from(tx.txo.txv, 'hex'),
@@ -375,6 +377,7 @@ export const generatePostTx = async (writer, externalNode, externalContractBoots
375
377
  b4a.from(tx.txo.mbs, 'hex')
376
378
  );
377
379
 
380
+ const postTx = safeEncodeApplyOperation(postTxPayload);
378
381
  return { postTx, txHash: tx.txo.tx };
379
382
  }
380
383
 
@@ -0,0 +1,12 @@
1
+ import { default as test } from 'brittle';
2
+
3
+ async function runMsgUtilsTests() {
4
+ test.pause();
5
+ await import('./network/NetworkMessageBuilder.test.js');
6
+ await import('./network/NetworkMessageDirector.test.js');
7
+ await import('./state/applyStateMessageBuilder.complete.test.js');
8
+ await import('./state/applyStateMessageBuilder.partial.test.js');
9
+ test.resume();
10
+ }
11
+
12
+ await runMsgUtilsTests();
@@ -0,0 +1,276 @@
1
+ import { test } from 'brittle';
2
+ import b4a from 'b4a';
3
+ import PeerWallet from 'trac-wallet';
4
+ import { v7 as uuidv7 } from 'uuid';
5
+ import NetworkWalletFactory from '../../../../src/core/network/identity/NetworkWalletFactory.js';
6
+ import NetworkMessageBuilder from '../../../../src/messages/network/v1/NetworkMessageBuilder.js';
7
+ import {
8
+ NetworkOperationType,
9
+ ResultCode as NetworkResultCode
10
+ } from '../../../../src/utils/constants.js';
11
+ import { decodeV1networkOperation, encodeV1networkOperation } from '../../../../src/utils/protobuf/operationHelpers.js';
12
+ import { errorMessageIncludes } from '../../../helpers/regexHelper.js';
13
+ import {
14
+ createMessage,
15
+ encodeCapabilities,
16
+ safeWriteUInt32BE,
17
+ idToBuffer,
18
+ timestampToBuffer
19
+ } from '../../../../src/utils/buffer.js';
20
+ import { addressToBuffer } from '../../../../src/core/state/utils/address.js';
21
+ import { config } from '../../../helpers/config.js';
22
+ import { asAddress } from '../../../helpers/address.js';
23
+ import { testKeyPair1 } from '../../../fixtures/apply.fixtures.js';
24
+
25
+ function createWallet() {
26
+ const keyPair = {
27
+ publicKey: b4a.from(testKeyPair1.publicKey, 'hex'),
28
+ secretKey: b4a.from(testKeyPair1.secretKey, 'hex')
29
+ };
30
+ return NetworkWalletFactory.provide({
31
+ enableWallet: false,
32
+ keyPair,
33
+ networkPrefix: config.addressPrefix
34
+ });
35
+ }
36
+
37
+ function uniqueResultCodes() {
38
+ return [...new Set(Object.values(NetworkResultCode))].sort((a, b) => a - b);
39
+ }
40
+
41
+ test('NetworkMessageBuilder builds validator connection request and verifies signature', async t => {
42
+ const wallet = createWallet();
43
+ const builder = new NetworkMessageBuilder(wallet, config);
44
+
45
+ const id = uuidv7();
46
+ const caps = ['cap:b', 'cap:a'];
47
+
48
+ await builder
49
+ .setType(NetworkOperationType.VALIDATOR_CONNECTION_REQUEST)
50
+ .setId(id)
51
+ .setTimestamp()
52
+ .setIssuerAddress(wallet.address)
53
+ .setCapabilities(caps)
54
+ .buildPayload();
55
+
56
+ const payload = builder.getResult();
57
+ t.is(payload.type, NetworkOperationType.VALIDATOR_CONNECTION_REQUEST);
58
+ t.is(payload.id, id);
59
+ t.alike(payload.capabilities, caps);
60
+ t.ok(b4a.isBuffer(payload.validator_connection_request.nonce));
61
+ t.ok(b4a.isBuffer(payload.validator_connection_request.signature));
62
+
63
+ const message = createMessage(
64
+ payload.type,
65
+ idToBuffer(id),
66
+ timestampToBuffer(payload.timestamp),
67
+ addressToBuffer(wallet.address, config.addressPrefix),
68
+ payload.validator_connection_request.nonce,
69
+ encodeCapabilities(caps)
70
+ );
71
+ const hash = await PeerWallet.blake3(message);
72
+ t.ok(wallet.verify(payload.validator_connection_request.signature, hash, wallet.publicKey));
73
+
74
+ const roundTrip = decodeV1networkOperation(encodeV1networkOperation(payload));
75
+ t.is(roundTrip.type, NetworkOperationType.VALIDATOR_CONNECTION_REQUEST);
76
+ });
77
+
78
+ test('NetworkMessageBuilder iterates validator connection response ResultCode values', async t => {
79
+ const wallet = createWallet();
80
+ const builder = new NetworkMessageBuilder(wallet, config);
81
+ const id = uuidv7();
82
+ const otherAddress = asAddress('36fdaf941de4afe602cbb1e2f56dc582466ef23fad1da55c09fd6dd841cbd117');
83
+ const caps = ['cap:b', 'cap:a'];
84
+
85
+ for (const code of uniqueResultCodes()) {
86
+ await builder
87
+ .setType(NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE)
88
+ .setId(id)
89
+ .setTimestamp()
90
+ .setIssuerAddress(otherAddress)
91
+ .setCapabilities(caps)
92
+ .setResultCode(code)
93
+ .buildPayload();
94
+
95
+ const payload = builder.getResult();
96
+ t.is(payload.type, NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE);
97
+ t.is(payload.validator_connection_response.result, code);
98
+
99
+ const msg = createMessage(
100
+ payload.type,
101
+ idToBuffer(payload.id),
102
+ timestampToBuffer(payload.timestamp),
103
+ addressToBuffer(otherAddress, config.addressPrefix),
104
+ payload.validator_connection_response.nonce,
105
+ safeWriteUInt32BE(code, 0),
106
+ encodeCapabilities(caps)
107
+ );
108
+ const hash = await PeerWallet.blake3(msg);
109
+ t.ok(wallet.verify(payload.validator_connection_response.signature, hash, wallet.publicKey));
110
+
111
+ const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
112
+ t.is(decoded.validator_connection_response.result, code);
113
+ }
114
+ });
115
+
116
+ test('NetworkMessageBuilder iterates liveness response ResultCode values', async t => {
117
+ const wallet = createWallet();
118
+ const builder = new NetworkMessageBuilder(wallet, config);
119
+ const id = uuidv7();
120
+ const caps = ['cap:b', 'cap:a'];
121
+ const data = b4a.from('ping', 'utf8');
122
+
123
+ for (const code of uniqueResultCodes()) {
124
+ await builder
125
+ .setType(NetworkOperationType.LIVENESS_RESPONSE)
126
+ .setId(id)
127
+ .setTimestamp()
128
+ .setData(data)
129
+ .setCapabilities(caps)
130
+ .setResultCode(code)
131
+ .buildPayload();
132
+
133
+ const payload = builder.getResult();
134
+ t.is(payload.type, NetworkOperationType.LIVENESS_RESPONSE);
135
+ t.is(payload.liveness_response.result, code);
136
+
137
+ const msg = createMessage(
138
+ payload.type,
139
+ idToBuffer(payload.id),
140
+ timestampToBuffer(payload.timestamp),
141
+ payload.liveness_response.nonce,
142
+ safeWriteUInt32BE(code, 0),
143
+ encodeCapabilities(caps)
144
+ );
145
+ const hash = await PeerWallet.blake3(msg);
146
+ t.ok(wallet.verify(payload.liveness_response.signature, hash, wallet.publicKey));
147
+
148
+ const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
149
+ t.is(decoded.liveness_response.result, code);
150
+ }
151
+ });
152
+
153
+ test('NetworkMessageBuilder builds liveness request and verifies signature (data not signed)', async t => {
154
+ const wallet = createWallet();
155
+ const builder = new NetworkMessageBuilder(wallet, config);
156
+
157
+ const id = uuidv7();
158
+ const caps = ['cap:b', 'cap:a'];
159
+ const data = b4a.from('ping', 'utf8');
160
+
161
+ await builder
162
+ .setType(NetworkOperationType.LIVENESS_REQUEST)
163
+ .setId(id)
164
+ .setTimestamp()
165
+ .setData(data)
166
+ .setCapabilities(caps)
167
+ .buildPayload();
168
+
169
+ const payload = builder.getResult();
170
+ t.is(payload.type, NetworkOperationType.LIVENESS_REQUEST);
171
+ t.ok(b4a.isBuffer(payload.liveness_request.nonce));
172
+ t.ok(b4a.isBuffer(payload.liveness_request.signature));
173
+
174
+ const msg = createMessage(
175
+ payload.type,
176
+ idToBuffer(payload.id),
177
+ timestampToBuffer(payload.timestamp),
178
+ payload.liveness_request.nonce,
179
+ encodeCapabilities(caps)
180
+ );
181
+ const hash = await PeerWallet.blake3(msg);
182
+ t.ok(wallet.verify(payload.liveness_request.signature, hash, wallet.publicKey));
183
+ });
184
+
185
+ test('NetworkMessageBuilder iterates broadcast transaction response ResultCode values', async t => {
186
+ const wallet = createWallet();
187
+ const builder = new NetworkMessageBuilder(wallet, config);
188
+ const id = uuidv7();
189
+ const caps = ['cap:b', 'cap:a'];
190
+
191
+ for (const code of uniqueResultCodes()) {
192
+ await builder
193
+ .setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
194
+ .setId(id)
195
+ .setTimestamp()
196
+ .setCapabilities(caps)
197
+ .setResultCode(code)
198
+ .buildPayload();
199
+
200
+ const payload = builder.getResult();
201
+ t.is(payload.type, NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE);
202
+ t.is(payload.broadcast_transaction_response.result, code);
203
+
204
+ const msg = createMessage(
205
+ payload.type,
206
+ idToBuffer(payload.id),
207
+ timestampToBuffer(payload.timestamp),
208
+ payload.broadcast_transaction_response.nonce,
209
+ safeWriteUInt32BE(code, 0),
210
+ encodeCapabilities(caps)
211
+ );
212
+ const hash = await PeerWallet.blake3(msg);
213
+ t.ok(wallet.verify(payload.broadcast_transaction_response.signature, hash, wallet.publicKey));
214
+
215
+ const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
216
+ t.is(decoded.broadcast_transaction_response.result, code);
217
+ }
218
+ });
219
+
220
+ test('NetworkMessageBuilder builds broadcast transaction request and verifies signature', async t => {
221
+ const wallet = createWallet();
222
+ const builder = new NetworkMessageBuilder(wallet, config);
223
+
224
+ const id = uuidv7();
225
+ const caps = ['cap:b', 'cap:a'];
226
+ const data = b4a.from('deadbeef', 'hex');
227
+
228
+ await builder
229
+ .setType(NetworkOperationType.BROADCAST_TRANSACTION_REQUEST)
230
+ .setId(id)
231
+ .setTimestamp()
232
+ .setData(data)
233
+ .setCapabilities(caps)
234
+ .buildPayload();
235
+
236
+ const payload = builder.getResult();
237
+ t.is(payload.type, NetworkOperationType.BROADCAST_TRANSACTION_REQUEST);
238
+ t.alike(payload.broadcast_transaction_request.data, data);
239
+
240
+ const msg = createMessage(
241
+ payload.type,
242
+ idToBuffer(payload.id),
243
+ timestampToBuffer(payload.timestamp),
244
+ data,
245
+ payload.broadcast_transaction_request.nonce,
246
+ encodeCapabilities(caps)
247
+ );
248
+ const hash = await PeerWallet.blake3(msg);
249
+ t.ok(wallet.verify(payload.broadcast_transaction_request.signature, hash, wallet.publicKey));
250
+ });
251
+
252
+ test('NetworkMessageBuilder validates required inputs', async t => {
253
+ const wallet = createWallet();
254
+ const builder = new NetworkMessageBuilder(wallet, config);
255
+ const id = uuidv7();
256
+ await t.exception(
257
+ () => builder.setType(undefined),
258
+ errorMessageIncludes('Invalid operation type')
259
+ );
260
+
261
+ await t.exception(
262
+ () => builder.setCapabilities('not-an-array'),
263
+ errorMessageIncludes('Capabilities must be a string array.')
264
+ );
265
+
266
+ await t.exception(
267
+ () =>
268
+ builder
269
+ .setType(NetworkOperationType.BROADCAST_TRANSACTION_REQUEST)
270
+ .setId(id)
271
+ .setTimestamp()
272
+ .setCapabilities([])
273
+ .buildPayload(),
274
+ errorMessageIncludes('Data must be set before building broadcast transaction request')
275
+ );
276
+ });