trac-msb 0.2.7 → 0.2.9

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 (187) hide show
  1. package/.github/workflows/publish.yml +8 -16
  2. package/msb.mjs +13 -25
  3. package/package.json +8 -4
  4. package/proto/network.proto +74 -0
  5. package/rpc/{create_server.mjs → create_server.js} +4 -4
  6. package/rpc/{handlers.mjs → handlers.js} +7 -7
  7. package/rpc/routes/{index.mjs → index.js} +1 -1
  8. package/rpc/routes/{v1.mjs → v1.js} +1 -1
  9. package/rpc/rpc_server.js +10 -0
  10. package/rpc/rpc_services.js +48 -7
  11. package/rpc/utils/{helpers.mjs → helpers.js} +1 -1
  12. package/src/config/config.js +137 -0
  13. package/src/config/env.js +63 -0
  14. package/src/core/network/Network.js +133 -119
  15. package/src/core/network/identity/NetworkWalletFactory.js +5 -6
  16. package/src/core/network/protocols/LegacyProtocol.js +67 -0
  17. package/src/core/network/protocols/NetworkMessages.js +48 -0
  18. package/src/core/network/protocols/ProtocolInterface.js +31 -0
  19. package/src/core/network/protocols/ProtocolSession.js +59 -0
  20. package/src/core/network/protocols/V1Protocol.js +64 -0
  21. package/src/core/network/protocols/legacy/NetworkMessageRouter.js +84 -0
  22. package/src/core/network/protocols/legacy/handlers/GetRequestHandler.js +53 -0
  23. package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
  24. package/src/core/network/{messaging → protocols/legacy}/validators/ValidatorResponse.js +2 -2
  25. package/src/core/network/{messaging → protocols/legacy}/validators/base/BaseResponse.js +13 -6
  26. package/src/core/network/protocols/shared/handlers/RoleOperationHandler.js +88 -0
  27. package/src/core/network/protocols/shared/handlers/SubnetworkOperationHandler.js +93 -0
  28. package/src/core/network/protocols/shared/handlers/TransferOperationHandler.js +57 -0
  29. package/src/core/network/{messaging → protocols/shared}/handlers/base/BaseOperationHandler.js +21 -26
  30. package/src/core/network/{messaging → protocols/shared}/validators/PartialBootstrapDeployment.js +3 -3
  31. package/src/core/network/{messaging → protocols/shared}/validators/PartialRoleAccess.js +15 -12
  32. package/src/core/network/{messaging → protocols/shared}/validators/PartialTransaction.js +10 -11
  33. package/src/core/network/{messaging → protocols/shared}/validators/PartialTransfer.js +10 -7
  34. package/src/core/network/{messaging → protocols/shared}/validators/base/PartialOperation.js +40 -22
  35. package/src/core/network/protocols/v1/NetworkMessageRouter.js +15 -0
  36. package/src/core/network/services/ConnectionManager.js +13 -19
  37. package/src/core/network/services/MessageOrchestrator.js +10 -22
  38. package/src/core/network/services/TransactionPoolService.js +10 -10
  39. package/src/core/network/services/TransactionRateLimiterService.js +5 -3
  40. package/src/core/network/services/ValidatorObserverService.js +46 -21
  41. package/src/core/state/State.js +137 -141
  42. package/src/core/state/utils/address.js +18 -16
  43. package/src/core/state/utils/adminEntry.js +17 -16
  44. package/src/core/state/utils/deploymentEntry.js +15 -15
  45. package/src/core/state/utils/transaction.js +3 -95
  46. package/src/index.js +250 -325
  47. package/src/messages/network/v1/NetworkMessageBuilder.js +325 -0
  48. package/src/messages/network/v1/NetworkMessageDirector.js +137 -0
  49. package/src/messages/network/v1/networkMessageFactory.js +12 -0
  50. package/src/messages/state/ApplyStateMessageBuilder.js +661 -0
  51. package/src/messages/state/ApplyStateMessageDirector.js +516 -0
  52. package/src/messages/state/applyStateMessageFactory.js +12 -0
  53. package/src/utils/buffer.js +53 -1
  54. package/src/utils/check.js +21 -17
  55. package/src/utils/cli.js +0 -8
  56. package/src/utils/cliCommands.js +11 -11
  57. package/src/utils/constants.js +36 -24
  58. package/src/utils/fileUtils.js +1 -4
  59. package/src/utils/helpers.js +9 -20
  60. package/src/utils/migrationUtils.js +2 -2
  61. package/src/utils/normalizers.js +94 -11
  62. package/src/utils/protobuf/network.cjs +840 -0
  63. package/src/utils/protobuf/operationHelpers.js +10 -0
  64. package/tests/acceptance/v1/account/account.test.mjs +2 -2
  65. package/tests/acceptance/v1/balance/balance.test.mjs +1 -1
  66. package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +11 -2
  67. package/tests/acceptance/v1/rpc.test.mjs +10 -10
  68. package/tests/acceptance/v1/tx/tx.test.mjs +4 -2
  69. package/tests/acceptance/v1/tx-details/tx-details.test.mjs +7 -3
  70. package/tests/fixtures/check.fixtures.js +42 -42
  71. package/tests/fixtures/networkV1.fixtures.js +84 -0
  72. package/tests/fixtures/protobuf.fixtures.js +110 -26
  73. package/tests/helpers/StateNetworkFactory.js +3 -5
  74. package/tests/helpers/autobaseTestHelpers.js +1 -2
  75. package/tests/helpers/config.js +3 -0
  76. package/tests/helpers/setupApplyTests.js +113 -99
  77. package/tests/helpers/transactionPayloads.mjs +26 -12
  78. package/tests/unit/messages/messages.test.js +12 -0
  79. package/tests/unit/messages/network/NetworkMessageBuilder.test.js +276 -0
  80. package/tests/unit/messages/network/NetworkMessageDirector.test.js +203 -0
  81. package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +521 -0
  82. package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +233 -0
  83. package/tests/unit/network/ConnectionManager.test.js +10 -7
  84. package/tests/unit/network/NetworkWalletFactory.test.js +14 -14
  85. package/tests/unit/network/networkModule.test.js +3 -2
  86. package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +10 -6
  87. package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +11 -8
  88. package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +11 -7
  89. package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +18 -20
  90. package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +57 -48
  91. package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +2 -1
  92. package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +72 -57
  93. package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +3 -7
  94. package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +12 -14
  95. package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +18 -13
  96. package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +2 -1
  97. package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +2 -1
  98. package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +27 -30
  99. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +2 -1
  100. package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +24 -21
  101. package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +5 -4
  102. package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +4 -3
  103. package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +2 -1
  104. package/tests/unit/state/apply/common/commonScenarioHelper.js +16 -16
  105. package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +10 -5
  106. package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +2 -2
  107. package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +2 -1
  108. package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +2 -1
  109. package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +2 -1
  110. package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +2 -1
  111. package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +16 -9
  112. package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +6 -5
  113. package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +23 -19
  114. package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +45 -36
  115. package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +48 -45
  116. package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +32 -29
  117. package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +2 -1
  118. package/tests/unit/state/stateModule.test.js +0 -1
  119. package/tests/unit/state/stateTestUtils.js +7 -3
  120. package/tests/unit/state/utils/address.test.js +3 -3
  121. package/tests/unit/state/utils/adminEntry.test.js +10 -9
  122. package/tests/unit/unit.test.js +1 -1
  123. package/tests/unit/utils/buffer/buffer.test.js +62 -1
  124. package/tests/unit/utils/check/adminControlOperation.test.js +3 -3
  125. package/tests/unit/utils/check/balanceInitializationOperation.test.js +2 -2
  126. package/tests/unit/utils/check/bootstrapDeploymentOperation.test.js +2 -3
  127. package/tests/unit/utils/check/common.test.js +7 -6
  128. package/tests/unit/utils/check/coreAdminOperation.test.js +3 -3
  129. package/tests/unit/utils/check/roleAccessOperation.test.js +3 -2
  130. package/tests/unit/utils/check/transactionOperation.test.js +3 -3
  131. package/tests/unit/utils/check/transferOperation.test.js +3 -3
  132. package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +2 -1
  133. package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +2 -1
  134. package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +7 -0
  135. package/tests/unit/utils/normalizers/normalizers.test.js +469 -0
  136. package/tests/unit/utils/protobuf/operationHelpers.test.js +120 -2
  137. package/tests/unit/utils/utils.test.js +0 -1
  138. package/rpc/rpc_server.mjs +0 -10
  139. package/src/core/network/messaging/NetworkMessages.js +0 -63
  140. package/src/core/network/messaging/handlers/GetRequestHandler.js +0 -112
  141. package/src/core/network/messaging/handlers/ResponseHandler.js +0 -108
  142. package/src/core/network/messaging/handlers/RoleOperationHandler.js +0 -116
  143. package/src/core/network/messaging/handlers/SubnetworkOperationHandler.js +0 -143
  144. package/src/core/network/messaging/handlers/TransferOperationHandler.js +0 -52
  145. package/src/core/network/messaging/routes/NetworkMessageRouter.js +0 -94
  146. package/src/core/network/messaging/validators/AdminResponse.js +0 -58
  147. package/src/core/network/messaging/validators/CustomNodeResponse.js +0 -46
  148. package/src/core/state/utils/indexerEntry.js +0 -105
  149. package/src/messages/base/StateBuilder.js +0 -25
  150. package/src/messages/completeStateMessages/CompleteStateMessageBuilder.js +0 -421
  151. package/src/messages/completeStateMessages/CompleteStateMessageDirector.js +0 -252
  152. package/src/messages/completeStateMessages/CompleteStateMessageOperations.js +0 -299
  153. package/src/messages/partialStateMessages/PartialStateMessageBuilder.js +0 -272
  154. package/src/messages/partialStateMessages/PartialStateMessageDirector.js +0 -137
  155. package/src/messages/partialStateMessages/PartialStateMessageOperations.js +0 -131
  156. package/src/utils/crypto.js +0 -11
  157. package/tests/integration/apply/addAdmin/addAdminBasic.test.js +0 -68
  158. package/tests/integration/apply/addAdmin/addAdminRecovery.test.js +0 -125
  159. package/tests/integration/apply/addIndexer.test.js +0 -237
  160. package/tests/integration/apply/addWhitelist.test.js +0 -53
  161. package/tests/integration/apply/addWriter.test.js +0 -244
  162. package/tests/integration/apply/apply.test.js +0 -19
  163. package/tests/integration/apply/banValidator.test.js +0 -109
  164. package/tests/integration/apply/postTx/invalidSubValues.test.js +0 -103
  165. package/tests/integration/apply/postTx/postTx.test.js +0 -222
  166. package/tests/integration/apply/removeIndexer.test.js +0 -128
  167. package/tests/integration/apply/removeWriter.test.js +0 -167
  168. package/tests/integration/apply/transfer.test.js +0 -81
  169. package/tests/integration/integration.test.js +0 -9
  170. package/tests/unit/messageOperations/assembleAddIndexerMessage.test.js +0 -21
  171. package/tests/unit/messageOperations/assembleAddWriterMessage.test.js +0 -16
  172. package/tests/unit/messageOperations/assembleAdminMessage.test.js +0 -69
  173. package/tests/unit/messageOperations/assembleBanWriterMessage.test.js +0 -16
  174. package/tests/unit/messageOperations/assemblePostTransaction.test.js +0 -442
  175. package/tests/unit/messageOperations/assembleRemoveIndexerMessage.test.js +0 -19
  176. package/tests/unit/messageOperations/assembleRemoveWriterMessage.test.js +0 -17
  177. package/tests/unit/messageOperations/assembleWhitelistMessages.test.js +0 -58
  178. package/tests/unit/messageOperations/commonsStateMessageOperationsTest.js +0 -277
  179. package/tests/unit/messageOperations/stateMessageOperations.test.js +0 -19
  180. package/tests/unit/state/utils/indexerEntry.test.js +0 -83
  181. package/tests/unit/state/utils/transaction.test.js +0 -97
  182. package/tests/unit/utils/crypto/createHash.test.js +0 -15
  183. /package/rpc/{constants.mjs → constants.js} +0 -0
  184. /package/rpc/{cors.mjs → cors.js} +0 -0
  185. /package/rpc/utils/{confirmedParameter.mjs → confirmedParameter.js} +0 -0
  186. /package/rpc/utils/{url.mjs → url.js} +0 -0
  187. /package/src/utils/{operations.js → applyOperations.js} +0 -0
@@ -0,0 +1,469 @@
1
+ import { test } from 'brittle';
2
+ import b4a from 'b4a';
3
+
4
+ import { config } from '../../../helpers/config.js';
5
+ import { randomAddress } from '../../state/stateTestUtils.js';
6
+ import { errorMessageIncludes } from '../../../helpers/regexHelper.js';
7
+ import { OperationType } from '../../../../src/utils/constants.js';
8
+ import {
9
+ normalizeBootstrapDeploymentOperation,
10
+ normalizeDecodedPayloadForJson,
11
+ normalizeRoleAccessOperation,
12
+ normalizeTransactionOperation,
13
+ normalizeTransferOperation
14
+ } from '../../../../src/utils/normalizers.js';
15
+ import { addressToBuffer } from '../../../../src/core/state/utils/address.js';
16
+ import { bigIntTo16ByteBuffer } from '../../../../src/utils/amountSerialization.js';
17
+
18
+ const hex = (value, bytes) => value.repeat(bytes);
19
+ const toBuffer = value => b4a.from(value, 'hex');
20
+
21
+ test('normalizeTransferOperation normalizes hex strings and addresses', t => {
22
+ const sender = randomAddress(config.addressPrefix);
23
+ const recipient = randomAddress(config.addressPrefix);
24
+ const tx = hex('11', 32);
25
+ const txv = hex('22', 32);
26
+ const nonce = hex('33', 32);
27
+ const amount = hex('44', 16);
28
+ const signature = hex('55', 64);
29
+
30
+ const payload = {
31
+ type: OperationType.TRANSFER,
32
+ address: sender,
33
+ tro: {
34
+ tx,
35
+ txv,
36
+ in: nonce,
37
+ to: recipient,
38
+ am: amount,
39
+ is: signature
40
+ }
41
+ };
42
+
43
+ const normalized = normalizeTransferOperation(payload, config);
44
+
45
+ t.is(normalized.type, OperationType.TRANSFER);
46
+ t.ok(b4a.equals(normalized.address, addressToBuffer(sender, config.addressPrefix)));
47
+ t.ok(b4a.equals(normalized.tro.tx, toBuffer(tx)));
48
+ t.ok(b4a.equals(normalized.tro.txv, toBuffer(txv)));
49
+ t.ok(b4a.equals(normalized.tro.in, toBuffer(nonce)));
50
+ t.ok(b4a.equals(normalized.tro.to, addressToBuffer(recipient, config.addressPrefix)));
51
+ t.ok(b4a.equals(normalized.tro.am, toBuffer(amount)));
52
+ t.ok(b4a.equals(normalized.tro.is, toBuffer(signature)));
53
+ });
54
+
55
+ test('normalizeTransferOperation accepts buffer inputs', t => {
56
+ const sender = randomAddress(config.addressPrefix);
57
+ const recipient = randomAddress(config.addressPrefix);
58
+ const tx = toBuffer(hex('aa', 32));
59
+ const txv = toBuffer(hex('bb', 32));
60
+ const nonce = toBuffer(hex('cc', 32));
61
+ const amount = toBuffer(hex('dd', 16));
62
+ const signature = toBuffer(hex('ee', 64));
63
+
64
+ const payload = {
65
+ type: OperationType.TRANSFER,
66
+ address: sender,
67
+ tro: {
68
+ tx,
69
+ txv,
70
+ in: nonce,
71
+ to: recipient,
72
+ am: amount,
73
+ is: signature
74
+ }
75
+ };
76
+
77
+ const normalized = normalizeTransferOperation(payload, config);
78
+ t.ok(b4a.equals(normalized.tro.tx, tx));
79
+ t.ok(b4a.equals(normalized.tro.txv, txv));
80
+ t.ok(b4a.equals(normalized.tro.in, nonce));
81
+ t.ok(b4a.equals(normalized.tro.am, amount));
82
+ t.ok(b4a.equals(normalized.tro.is, signature));
83
+ });
84
+
85
+ test('normalizeTransferOperation throws on missing payload fields', t => {
86
+ t.exception(
87
+ () => normalizeTransferOperation({ type: OperationType.TRANSFER }, config),
88
+ errorMessageIncludes('Invalid payload for transfer operation normalization.')
89
+ );
90
+
91
+ t.exception(
92
+ () => normalizeTransferOperation({ type: OperationType.TX, address: 'x', tro: {} }, config),
93
+ errorMessageIncludes('Missing required fields in transfer operation payload.')
94
+ );
95
+
96
+ const sender = randomAddress(config.addressPrefix);
97
+ const payload = {
98
+ type: OperationType.TRANSFER,
99
+ address: sender,
100
+ tro: {
101
+ tx: hex('11', 32),
102
+ txv: hex('22', 32),
103
+ in: hex('33', 32),
104
+ to: randomAddress(config.addressPrefix),
105
+ am: hex('44', 16)
106
+ }
107
+ };
108
+ t.exception(
109
+ () => normalizeTransferOperation(payload, config),
110
+ errorMessageIncludes('Missing required fields in transfer operation payload.')
111
+ );
112
+ });
113
+
114
+ test('normalizeTransferOperation throws on invalid hex string', t => {
115
+ const sender = randomAddress(config.addressPrefix);
116
+ const payload = {
117
+ type: OperationType.TRANSFER,
118
+ address: sender,
119
+ tro: {
120
+ tx: 'zz',
121
+ txv: hex('22', 32),
122
+ in: hex('33', 32),
123
+ to: randomAddress(config.addressPrefix),
124
+ am: hex('44', 16),
125
+ is: hex('55', 64)
126
+ }
127
+ };
128
+ t.exception(
129
+ () => normalizeTransferOperation(payload, config),
130
+ errorMessageIncludes('Invalid hex string')
131
+ );
132
+ });
133
+
134
+ test('normalizeTransactionOperation normalizes hex strings and addresses', t => {
135
+ const sender = randomAddress(config.addressPrefix);
136
+ const tx = hex('11', 32);
137
+ const txv = hex('22', 32);
138
+ const writerKey = hex('33', 32);
139
+ const contentHash = hex('44', 32);
140
+ const bootstrap = hex('55', 32);
141
+ const msbBootstrap = hex('66', 32);
142
+ const nonce = hex('77', 32);
143
+ const signature = hex('88', 64);
144
+
145
+ const payload = {
146
+ type: OperationType.TX,
147
+ address: sender,
148
+ txo: {
149
+ tx,
150
+ txv,
151
+ iw: writerKey,
152
+ ch: contentHash,
153
+ bs: bootstrap,
154
+ mbs: msbBootstrap,
155
+ in: nonce,
156
+ is: signature
157
+ }
158
+ };
159
+
160
+ const normalized = normalizeTransactionOperation(payload, config);
161
+ t.is(normalized.type, OperationType.TX);
162
+ t.ok(b4a.equals(normalized.address, addressToBuffer(sender, config.addressPrefix)));
163
+ t.ok(b4a.equals(normalized.txo.tx, toBuffer(tx)));
164
+ t.ok(b4a.equals(normalized.txo.txv, toBuffer(txv)));
165
+ t.ok(b4a.equals(normalized.txo.iw, toBuffer(writerKey)));
166
+ t.ok(b4a.equals(normalized.txo.ch, toBuffer(contentHash)));
167
+ t.ok(b4a.equals(normalized.txo.bs, toBuffer(bootstrap)));
168
+ t.ok(b4a.equals(normalized.txo.mbs, toBuffer(msbBootstrap)));
169
+ t.ok(b4a.equals(normalized.txo.in, toBuffer(nonce)));
170
+ t.ok(b4a.equals(normalized.txo.is, toBuffer(signature)));
171
+ });
172
+
173
+ test('normalizeTransactionOperation accepts buffer inputs', t => {
174
+ const sender = randomAddress(config.addressPrefix);
175
+ const tx = toBuffer(hex('aa', 32));
176
+ const txv = toBuffer(hex('bb', 32));
177
+ const writerKey = toBuffer(hex('cc', 32));
178
+ const contentHash = toBuffer(hex('dd', 32));
179
+ const bootstrap = toBuffer(hex('ee', 32));
180
+ const msbBootstrap = toBuffer(hex('ff', 32));
181
+ const nonce = toBuffer(hex('12', 32));
182
+ const signature = toBuffer(hex('34', 64));
183
+
184
+ const payload = {
185
+ type: OperationType.TX,
186
+ address: sender,
187
+ txo: {
188
+ tx,
189
+ txv,
190
+ iw: writerKey,
191
+ ch: contentHash,
192
+ bs: bootstrap,
193
+ mbs: msbBootstrap,
194
+ in: nonce,
195
+ is: signature
196
+ }
197
+ };
198
+
199
+ const normalized = normalizeTransactionOperation(payload, config);
200
+ t.ok(b4a.equals(normalized.txo.tx, tx));
201
+ t.ok(b4a.equals(normalized.txo.txv, txv));
202
+ t.ok(b4a.equals(normalized.txo.iw, writerKey));
203
+ t.ok(b4a.equals(normalized.txo.ch, contentHash));
204
+ t.ok(b4a.equals(normalized.txo.bs, bootstrap));
205
+ t.ok(b4a.equals(normalized.txo.mbs, msbBootstrap));
206
+ t.ok(b4a.equals(normalized.txo.in, nonce));
207
+ t.ok(b4a.equals(normalized.txo.is, signature));
208
+ });
209
+
210
+ test('normalizeTransactionOperation throws on missing payload fields', t => {
211
+ t.exception(
212
+ () => normalizeTransactionOperation({ type: OperationType.TX }, config),
213
+ errorMessageIncludes('Invalid payload for transaction operation normalization.')
214
+ );
215
+
216
+ t.exception(
217
+ () => normalizeTransactionOperation({ type: OperationType.TRANSFER, address: 'x', txo: {} }, config),
218
+ errorMessageIncludes('Missing required fields in transaction operation payload.')
219
+ );
220
+
221
+ const sender = randomAddress(config.addressPrefix);
222
+ const payload = {
223
+ type: OperationType.TX,
224
+ address: sender,
225
+ txo: {
226
+ tx: hex('11', 32),
227
+ txv: hex('22', 32),
228
+ iw: hex('33', 32),
229
+ ch: hex('44', 32),
230
+ bs: hex('55', 32),
231
+ mbs: hex('66', 32),
232
+ in: hex('77', 32)
233
+ }
234
+ };
235
+ t.exception(
236
+ () => normalizeTransactionOperation(payload, config),
237
+ errorMessageIncludes('Missing required fields in transaction operation payload.')
238
+ );
239
+ });
240
+
241
+ test('normalizeTransactionOperation throws on invalid hex string', t => {
242
+ const sender = randomAddress(config.addressPrefix);
243
+ const payload = {
244
+ type: OperationType.TX,
245
+ address: sender,
246
+ txo: {
247
+ tx: 'zz',
248
+ txv: hex('22', 32),
249
+ iw: hex('33', 32),
250
+ ch: hex('44', 32),
251
+ bs: hex('55', 32),
252
+ mbs: hex('66', 32),
253
+ in: hex('77', 32),
254
+ is: hex('88', 64)
255
+ }
256
+ };
257
+ t.exception(
258
+ () => normalizeTransactionOperation(payload, config),
259
+ errorMessageIncludes('Invalid hex string')
260
+ );
261
+ });
262
+
263
+ test('normalizeRoleAccessOperation normalizes hex strings and addresses', t => {
264
+ const sender = randomAddress(config.addressPrefix);
265
+ const payload = {
266
+ type: OperationType.ADD_WRITER,
267
+ address: sender,
268
+ rao: {
269
+ tx: hex('11', 32),
270
+ txv: hex('22', 32),
271
+ iw: hex('33', 32),
272
+ in: hex('44', 32),
273
+ is: hex('55', 64)
274
+ }
275
+ };
276
+
277
+ const normalized = normalizeRoleAccessOperation(payload, config);
278
+ t.is(normalized.type, OperationType.ADD_WRITER);
279
+ t.ok(b4a.equals(normalized.address, addressToBuffer(sender, config.addressPrefix)));
280
+ t.ok(b4a.equals(normalized.rao.tx, toBuffer(hex('11', 32))));
281
+ t.ok(b4a.equals(normalized.rao.txv, toBuffer(hex('22', 32))));
282
+ t.ok(b4a.equals(normalized.rao.iw, toBuffer(hex('33', 32))));
283
+ t.ok(b4a.equals(normalized.rao.in, toBuffer(hex('44', 32))));
284
+ t.ok(b4a.equals(normalized.rao.is, toBuffer(hex('55', 64))));
285
+ });
286
+
287
+ test('normalizeRoleAccessOperation accepts buffer inputs', t => {
288
+ const sender = randomAddress(config.addressPrefix);
289
+ const tx = toBuffer(hex('aa', 32));
290
+ const txv = toBuffer(hex('bb', 32));
291
+ const writerKey = toBuffer(hex('cc', 32));
292
+ const nonce = toBuffer(hex('dd', 32));
293
+ const signature = toBuffer(hex('ee', 64));
294
+
295
+ const payload = {
296
+ type: OperationType.REMOVE_WRITER,
297
+ address: sender,
298
+ rao: {
299
+ tx,
300
+ txv,
301
+ iw: writerKey,
302
+ in: nonce,
303
+ is: signature
304
+ }
305
+ };
306
+
307
+ const normalized = normalizeRoleAccessOperation(payload, config);
308
+ t.ok(b4a.equals(normalized.rao.tx, tx));
309
+ t.ok(b4a.equals(normalized.rao.txv, txv));
310
+ t.ok(b4a.equals(normalized.rao.iw, writerKey));
311
+ t.ok(b4a.equals(normalized.rao.in, nonce));
312
+ t.ok(b4a.equals(normalized.rao.is, signature));
313
+ });
314
+
315
+ test('normalizeRoleAccessOperation throws on missing payload fields', t => {
316
+ t.exception(
317
+ () => normalizeRoleAccessOperation({ type: OperationType.ADD_WRITER }, config),
318
+ errorMessageIncludes('Invalid payload for role access normalization.')
319
+ );
320
+
321
+ t.exception(
322
+ () => normalizeRoleAccessOperation({ type: OperationType.ADD_WRITER, address: 'x', rao: {} }, config),
323
+ errorMessageIncludes('Missing required fields in role access payload.')
324
+ );
325
+ });
326
+
327
+ test('normalizeRoleAccessOperation throws on invalid hex string', t => {
328
+ const sender = randomAddress(config.addressPrefix);
329
+ const payload = {
330
+ type: OperationType.ADD_WRITER,
331
+ address: sender,
332
+ rao: {
333
+ tx: 'zz',
334
+ txv: hex('22', 32),
335
+ iw: hex('33', 32),
336
+ in: hex('44', 32),
337
+ is: hex('55', 64)
338
+ }
339
+ };
340
+ t.exception(
341
+ () => normalizeRoleAccessOperation(payload, config),
342
+ errorMessageIncludes('Invalid hex string')
343
+ );
344
+ });
345
+
346
+ test('normalizeBootstrapDeploymentOperation normalizes hex strings and addresses', t => {
347
+ const sender = randomAddress(config.addressPrefix);
348
+ const payload = {
349
+ type: OperationType.BOOTSTRAP_DEPLOYMENT,
350
+ address: sender,
351
+ bdo: {
352
+ tx: hex('11', 32),
353
+ txv: hex('22', 32),
354
+ bs: hex('33', 32),
355
+ ic: hex('44', 32),
356
+ in: hex('55', 32),
357
+ is: hex('66', 64)
358
+ }
359
+ };
360
+
361
+ const normalized = normalizeBootstrapDeploymentOperation(payload, config);
362
+ t.is(normalized.type, OperationType.BOOTSTRAP_DEPLOYMENT);
363
+ t.ok(b4a.equals(normalized.address, addressToBuffer(sender, config.addressPrefix)));
364
+ t.ok(b4a.equals(normalized.bdo.tx, toBuffer(hex('11', 32))));
365
+ t.ok(b4a.equals(normalized.bdo.txv, toBuffer(hex('22', 32))));
366
+ t.ok(b4a.equals(normalized.bdo.bs, toBuffer(hex('33', 32))));
367
+ t.ok(b4a.equals(normalized.bdo.ic, toBuffer(hex('44', 32))));
368
+ t.ok(b4a.equals(normalized.bdo.in, toBuffer(hex('55', 32))));
369
+ t.ok(b4a.equals(normalized.bdo.is, toBuffer(hex('66', 64))));
370
+ });
371
+
372
+ test('normalizeBootstrapDeploymentOperation accepts buffer inputs', t => {
373
+ const sender = randomAddress(config.addressPrefix);
374
+ const tx = toBuffer(hex('aa', 32));
375
+ const txv = toBuffer(hex('bb', 32));
376
+ const bootstrap = toBuffer(hex('cc', 32));
377
+ const channel = toBuffer(hex('dd', 32));
378
+ const nonce = toBuffer(hex('ee', 32));
379
+ const signature = toBuffer(hex('ff', 64));
380
+
381
+ const payload = {
382
+ type: OperationType.BOOTSTRAP_DEPLOYMENT,
383
+ address: sender,
384
+ bdo: {
385
+ tx,
386
+ txv,
387
+ bs: bootstrap,
388
+ ic: channel,
389
+ in: nonce,
390
+ is: signature
391
+ }
392
+ };
393
+
394
+ const normalized = normalizeBootstrapDeploymentOperation(payload, config);
395
+ t.ok(b4a.equals(normalized.bdo.tx, tx));
396
+ t.ok(b4a.equals(normalized.bdo.txv, txv));
397
+ t.ok(b4a.equals(normalized.bdo.bs, bootstrap));
398
+ t.ok(b4a.equals(normalized.bdo.ic, channel));
399
+ t.ok(b4a.equals(normalized.bdo.in, nonce));
400
+ t.ok(b4a.equals(normalized.bdo.is, signature));
401
+ });
402
+
403
+ test('normalizeBootstrapDeploymentOperation throws on missing payload fields', t => {
404
+ t.exception(
405
+ () => normalizeBootstrapDeploymentOperation({ type: OperationType.BOOTSTRAP_DEPLOYMENT }, config),
406
+ errorMessageIncludes('Invalid payload for bootstrap deployment normalization.')
407
+ );
408
+
409
+ t.exception(
410
+ () => normalizeBootstrapDeploymentOperation({ type: OperationType.TX, address: 'x', bdo: {} }, config),
411
+ errorMessageIncludes('Missing required fields in bootstrap deployment payload.')
412
+ );
413
+ });
414
+
415
+ test('normalizeBootstrapDeploymentOperation throws on invalid hex string', t => {
416
+ const sender = randomAddress(config.addressPrefix);
417
+ const payload = {
418
+ type: OperationType.BOOTSTRAP_DEPLOYMENT,
419
+ address: sender,
420
+ bdo: {
421
+ tx: 'zz',
422
+ txv: hex('22', 32),
423
+ bs: hex('33', 32),
424
+ ic: hex('44', 32),
425
+ in: hex('55', 32),
426
+ is: hex('66', 64)
427
+ }
428
+ };
429
+ t.exception(
430
+ () => normalizeBootstrapDeploymentOperation(payload, config),
431
+ errorMessageIncludes('Invalid hex string')
432
+ );
433
+ });
434
+
435
+ test('normalizeDecodedPayloadForJson converts buffers to strings', t => {
436
+ const address = randomAddress(config.addressPrefix);
437
+ const addressBuf = addressToBuffer(address, config.addressPrefix);
438
+ const amountBuf = bigIntTo16ByteBuffer(1234n);
439
+ const otherBuf = b4a.from('abcd', 'hex');
440
+
441
+ const payload = {
442
+ address: addressBuf,
443
+ am: amountBuf,
444
+ nested: {
445
+ to: addressBuf,
446
+ amount: amountBuf,
447
+ other: otherBuf
448
+ }
449
+ };
450
+
451
+ const normalized = normalizeDecodedPayloadForJson(payload, config);
452
+ t.is(normalized.address, address);
453
+ t.is(normalized.am, '1234');
454
+ t.is(normalized.nested.to, address);
455
+ t.is(normalized.nested.amount, '1234');
456
+ t.is(normalized.nested.other, 'abcd');
457
+ });
458
+
459
+ test('normalizeDecodedPayloadForJson falls back to hex for invalid address buffers', t => {
460
+ const bad = b4a.from('deadbeef', 'hex');
461
+ const payload = { address: bad };
462
+ const normalized = normalizeDecodedPayloadForJson(payload, config);
463
+ t.is(normalized.address, b4a.toString(bad, 'hex'));
464
+ });
465
+
466
+ test('normalizeDecodedPayloadForJson returns input for non-objects', t => {
467
+ t.is(normalizeDecodedPayloadForJson(null, config), null);
468
+ t.is(normalizeDecodedPayloadForJson('value', config), 'value');
469
+ });
@@ -2,12 +2,20 @@ import test from 'brittle';
2
2
  import b4a from 'b4a';
3
3
 
4
4
  import applyOperations from '../../../../src/utils/protobuf/applyOperations.cjs';
5
+ import {
6
+ decodeV1networkOperation,
7
+ encodeV1networkOperation,
8
+ normalizeIncomingMessage,
9
+ safeDecodeApplyOperation,
10
+ safeEncodeApplyOperation,
11
+ } from '../../../../src/utils/protobuf/operationHelpers.js';
5
12
  import fixtures from '../../../fixtures/protobuf.fixtures.js';
13
+ import networkV1Fixtures from '../../../fixtures/networkV1.fixtures.js';
6
14
 
7
- //TODO add missing operations tests and fill fixtures with them
8
15
  test('Happy path encode/decode roundtrip for protobuf applyOperation payloads', t => {
9
16
  const payloadsHashMap = new Map([
10
17
  ["txComplete", fixtures.validTransactionOperation],
18
+ ["txPartial", fixtures.validPartialTransactionOperation],
11
19
  ["addIndexer", fixtures.validAddIndexer],
12
20
  ["removeIndexer", fixtures.validRemoveIndexer],
13
21
  ["appendWhitelist", fixtures.validAppendWhitelist],
@@ -19,8 +27,12 @@ test('Happy path encode/decode roundtrip for protobuf applyOperation payloads',
19
27
  ["removeWriterPartial", fixtures.validPartialRemoveWriter],
20
28
  ["adminRecoveryComplete", fixtures.validCompleteAdminRecovery],
21
29
  ["adminRecoveryPartial", fixtures.validPartialAdminRecovery],
30
+ ["bootstrapDeploymentComplete", fixtures.validCompleteBootstrapDeployment],
31
+ ["bootstrapDeploymentPartial", fixtures.validPartialBootstrapDeployment],
22
32
  ["transferComplete", fixtures.validTransferOperation],
23
- ["balanceInitialization", fixtures.validBalanceInitOperation]
33
+ ["transferPartial", fixtures.validPartialTransferOperation],
34
+ ["balanceInitialization", fixtures.validBalanceInitOperation],
35
+ ["disableInitialization", fixtures.validDisableInitialization],
24
36
  ]);
25
37
 
26
38
  for (const [key, value] of payloadsHashMap) {
@@ -101,6 +113,13 @@ test('Protobuf encode/decode is order-independent for all operation types', t =>
101
113
  let decoded = applyOperations.Operation.decode(encoded);
102
114
  t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validTransactionOperation), 'TX operation encodes/decodes correctly with shuffled fields');
103
115
 
116
+ // Test TX operation (partial)
117
+ const shuffledPartialTxo = shuffleObject(fixtures.validPartialTransactionOperation.txo);
118
+ const shuffledPartialTx = { ...fixtures.validPartialTransactionOperation, txo: shuffledPartialTxo };
119
+ encoded = applyOperations.Operation.encode(shuffledPartialTx);
120
+ decoded = applyOperations.Operation.decode(encoded);
121
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialTransactionOperation), 'Partial TX operation encodes/decodes correctly with shuffled fields');
122
+
104
123
  // Test TRANSFER operation
105
124
  const shuffledTro = shuffleObject(fixtures.validTransferOperation.tro);
106
125
  const shuffledTransfer = { ...fixtures.validTransferOperation, tro: shuffledTro };
@@ -108,6 +127,13 @@ test('Protobuf encode/decode is order-independent for all operation types', t =>
108
127
  decoded = applyOperations.Operation.decode(encoded);
109
128
  t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validTransferOperation), 'TRANSFER operation encodes/decodes correctly with shuffled fields');
110
129
 
130
+ // Test TRANSFER operation (partial)
131
+ const shuffledPartialTro = shuffleObject(fixtures.validPartialTransferOperation.tro);
132
+ const shuffledPartialTransfer = { ...fixtures.validPartialTransferOperation, tro: shuffledPartialTro };
133
+ encoded = applyOperations.Operation.encode(shuffledPartialTransfer);
134
+ decoded = applyOperations.Operation.decode(encoded);
135
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialTransferOperation), 'Partial TRANSFER operation encodes/decodes correctly with shuffled fields');
136
+
111
137
  // Test ADD_INDEXER operation
112
138
  const shuffledAco = shuffleObject(fixtures.validAddIndexer.aco);
113
139
  const shuffledAddIndexer = { ...fixtures.validAddIndexer, aco: shuffledAco };
@@ -184,4 +210,96 @@ test('Protobuf encode/decode is order-independent for all operation types', t =>
184
210
  encoded = applyOperations.Operation.encode(shuffledPartialAdminRecovery);
185
211
  decoded = applyOperations.Operation.decode(encoded);
186
212
  t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialAdminRecovery), 'Partial ADMIN_RECOVERY operation encodes/decodes correctly with shuffled fields');
213
+
214
+ // Test BOOTSTRAP_DEPLOYMENT (complete) operation
215
+ const shuffledCompleteBootstrapBdo = shuffleObject(fixtures.validCompleteBootstrapDeployment.bdo);
216
+ const shuffledCompleteBootstrap = { ...fixtures.validCompleteBootstrapDeployment, bdo: shuffledCompleteBootstrapBdo };
217
+ encoded = applyOperations.Operation.encode(shuffledCompleteBootstrap);
218
+ decoded = applyOperations.Operation.decode(encoded);
219
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validCompleteBootstrapDeployment), 'Complete BOOTSTRAP_DEPLOYMENT operation encodes/decodes correctly with shuffled fields');
220
+
221
+ // Test BOOTSTRAP_DEPLOYMENT (partial) operation
222
+ const shuffledPartialBootstrapBdo = shuffleObject(fixtures.validPartialBootstrapDeployment.bdo);
223
+ const shuffledPartialBootstrap = { ...fixtures.validPartialBootstrapDeployment, bdo: shuffledPartialBootstrapBdo };
224
+ encoded = applyOperations.Operation.encode(shuffledPartialBootstrap);
225
+ decoded = applyOperations.Operation.decode(encoded);
226
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validPartialBootstrapDeployment), 'Partial BOOTSTRAP_DEPLOYMENT operation encodes/decodes correctly with shuffled fields');
227
+
228
+ // Test BALANCE_INITIALIZATION operation
229
+ const shuffledBio = shuffleObject(fixtures.validBalanceInitOperation.bio);
230
+ const shuffledBalanceInit = { ...fixtures.validBalanceInitOperation, bio: shuffledBio };
231
+ encoded = applyOperations.Operation.encode(shuffledBalanceInit);
232
+ decoded = applyOperations.Operation.decode(encoded);
233
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validBalanceInitOperation), 'BALANCE_INITIALIZATION operation encodes/decodes correctly with shuffled fields');
234
+
235
+ // Test DISABLE_INITIALIZATION operation
236
+ const shuffledDisableCao = shuffleObject(fixtures.validDisableInitialization.cao);
237
+ const shuffledDisableInitialization = { ...fixtures.validDisableInitialization, cao: shuffledDisableCao };
238
+ encoded = applyOperations.Operation.encode(shuffledDisableInitialization);
239
+ decoded = applyOperations.Operation.decode(encoded);
240
+ t.ok(JSON.stringify(decoded) === JSON.stringify(fixtures.validDisableInitialization), 'DISABLE_INITIALIZATION operation encodes/decodes correctly with shuffled fields');
241
+ });
242
+
243
+ test('encodeV1networkOperation/decodeV1networkOperation roundtrip for network v1 payloads', t => {
244
+ const payloadsHashMap = new Map([
245
+ ['validatorConnectionRequest', networkV1Fixtures.payloadValidatorConnectionRequest],
246
+ ['validatorConnectionResponse', networkV1Fixtures.payloadValidatorConnectionResponse],
247
+ ['livenessRequest', networkV1Fixtures.payloadLivenessRequest],
248
+ ['livenessResponse', networkV1Fixtures.payloadLivenessResponse],
249
+ ['broadcastTransactionRequest', networkV1Fixtures.payloadBroadcastTransactionRequest],
250
+ ['broadcastTransactionResponse', networkV1Fixtures.payloadBroadcastTransactionResponse],
251
+ ]);
252
+
253
+ for (const [key, payload] of payloadsHashMap) {
254
+ const encoded = encodeV1networkOperation(payload);
255
+ const decoded = decodeV1networkOperation(encoded);
256
+ t.ok(b4a.isBuffer(encoded) && encoded.length > 0, `Payload ${key} encodes to a non-empty buffer`);
257
+ t.ok(JSON.stringify(decoded) === JSON.stringify(payload), `Payload ${key} decodes back correctly`);
258
+ }
259
+ });
260
+
261
+ test('safeEncodeApplyOperation returns an empty buffer on encode errors (and does not throw)', t => {
262
+ const originalLog = console.log;
263
+ console.log = () => {};
264
+ try {
265
+ const encoded = safeEncodeApplyOperation(fixtures.invalidPayloadWithMultipleOneOfKeys);
266
+ t.ok(b4a.isBuffer(encoded));
267
+ t.is(encoded.length, 0);
268
+ } finally {
269
+ console.log = originalLog;
270
+ }
271
+ });
272
+
273
+ test('safeEncodeApplyOperation encodes valid payloads to a non-empty buffer', t => {
274
+ const encoded = safeEncodeApplyOperation(fixtures.validTransactionOperation);
275
+ t.ok(b4a.isBuffer(encoded));
276
+ t.ok(encoded.length > 0);
277
+ });
278
+
279
+ test('safeDecodeApplyOperation returns null for invalid input (and does not throw)', t => {
280
+ t.is(safeDecodeApplyOperation(null), null);
281
+ t.is(safeDecodeApplyOperation({}), null);
282
+ t.is(safeDecodeApplyOperation('not-a-buffer'), null);
283
+
284
+ const originalLog = console.log;
285
+ console.log = () => {};
286
+ try {
287
+ t.is(safeDecodeApplyOperation(b4a.from([0x0F])), null);
288
+ } finally {
289
+ console.log = originalLog;
290
+ }
291
+ });
292
+
293
+ test('normalizeIncomingMessage decodes buffers and JSON buffers', t => {
294
+ const payload = fixtures.validTransactionOperation;
295
+ const encoded = applyOperations.Operation.encode(payload);
296
+
297
+ const decodedFromBuffer = normalizeIncomingMessage(encoded);
298
+ t.ok(JSON.stringify(decodedFromBuffer) === JSON.stringify(payload));
299
+
300
+ const decodedFromJsonBuffer = normalizeIncomingMessage({ type: 'Buffer', data: Array.from(encoded) });
301
+ t.ok(JSON.stringify(decodedFromJsonBuffer) === JSON.stringify(payload));
302
+
303
+ t.is(normalizeIncomingMessage(null), null);
304
+ t.is(normalizeIncomingMessage({ type: 'nope', data: [] }), null);
187
305
  });
@@ -13,7 +13,6 @@ async function runTests() {
13
13
  await import('./migrationUtils/validateAddressFromIncomingFile.test.js');
14
14
  await import('./buffer/buffer.test.js')
15
15
  await import('./amountSerialization/amountSerialization.test.js');
16
- await import('./crypto/createHash.test.js');
17
16
  test.resume();
18
17
  }
19
18
 
@@ -1,10 +0,0 @@
1
- import { createServer } from "./create_server.mjs";
2
-
3
- // Called by msb.mjs file
4
- export function startRpcServer(msbInstance, host, port) {
5
- const server = createServer(msbInstance)
6
-
7
- return server.listen(port, host, () => {
8
- console.log(`Running RPC with http at http://${host}:${port}`);
9
- });
10
- }