trac-msb 0.2.13 → 0.2.15
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.
- package/.github/workflows/acceptance-tests.yml +38 -0
- package/.github/workflows/lint-pr-title.yml +26 -0
- package/.github/workflows/publish.yml +33 -0
- package/.github/workflows/unit-tests.yml +34 -0
- package/package.json +7 -12
- package/proto/network.proto +74 -0
- package/rpc/rpc_services.js +4 -22
- package/scripts/generate-protobufs.js +12 -37
- package/src/config/config.js +9 -26
- package/src/config/env.js +17 -27
- package/src/core/network/Network.js +36 -73
- package/src/core/network/protocols/LegacyProtocol.js +11 -21
- package/src/core/network/protocols/NetworkMessages.js +17 -38
- package/src/core/network/protocols/ProtocolInterface.js +2 -14
- package/src/core/network/protocols/ProtocolSession.js +17 -144
- package/src/core/network/protocols/V1Protocol.js +18 -37
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +19 -25
- package/src/core/network/protocols/legacy/handlers/{LegacyGetRequestHandler.js → GetRequestHandler.js} +6 -6
- package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
- package/src/core/network/protocols/{legacy/handlers/LegacyRoleOperationHandler.js → shared/handlers/RoleOperationHandler.js} +11 -18
- package/src/core/network/protocols/{legacy/handlers/LegacySubnetworkOperationHandler.js → shared/handlers/SubnetworkOperationHandler.js} +17 -28
- package/src/core/network/protocols/{legacy/handlers/LegacyTransferOperationHandler.js → shared/handlers/TransferOperationHandler.js} +11 -17
- package/src/core/network/protocols/{legacy/handlers/BaseStateOperationHandler.js → shared/handlers/base/BaseOperationHandler.js} +12 -23
- package/src/core/network/protocols/shared/validators/{PartialBootstrapDeploymentValidator.js → PartialBootstrapDeployment.js} +4 -9
- package/src/core/network/protocols/shared/validators/{PartialRoleAccessValidator.js → PartialRoleAccess.js} +17 -51
- package/src/core/network/protocols/shared/validators/{PartialTransactionValidator.js → PartialTransaction.js} +7 -21
- package/src/core/network/protocols/shared/validators/{PartialTransferValidator.js → PartialTransfer.js} +9 -26
- package/src/core/network/protocols/shared/validators/{PartialOperationValidator.js → base/PartialOperation.js} +25 -47
- package/src/core/network/protocols/v1/NetworkMessageRouter.js +7 -91
- package/src/core/network/services/ConnectionManager.js +94 -146
- package/src/core/network/services/MessageOrchestrator.js +27 -151
- package/src/core/network/services/TransactionPoolService.js +18 -129
- package/src/core/network/services/TransactionRateLimiterService.js +34 -52
- package/src/core/network/services/ValidatorObserverService.js +26 -18
- package/src/core/state/State.js +19 -70
- package/src/index.js +8 -6
- package/src/messages/network/v1/NetworkMessageBuilder.js +79 -59
- package/src/messages/network/v1/NetworkMessageDirector.js +50 -16
- package/src/utils/Scheduler.js +8 -0
- package/src/utils/constants.js +5 -71
- package/src/utils/helpers.js +1 -10
- package/src/utils/normalizers.js +0 -38
- package/src/utils/protobuf/network.cjs +840 -0
- package/src/utils/protobuf/operationHelpers.js +3 -24
- package/tests/acceptance/v1/account/account.test.mjs +2 -8
- package/tests/acceptance/v1/tx/tx.test.mjs +1 -23
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +6 -34
- package/tests/fixtures/assembleMessage.fixtures.js +8 -7
- package/tests/fixtures/networkV1.fixtures.js +28 -2
- package/tests/helpers/autobaseTestHelpers.js +5 -2
- package/tests/helpers/createTestSignature.js +3 -2
- package/tests/helpers/transactionPayloads.mjs +2 -2
- package/tests/unit/messages/network/NetworkMessageBuilder.test.js +79 -239
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +77 -223
- package/tests/unit/messages/state/applyStateMessageBuilder.complete.test.js +5 -1
- package/tests/unit/messages/state/applyStateMessageBuilder.partial.test.js +5 -1
- package/tests/unit/network/ConnectionManager.test.js +191 -0
- package/tests/unit/network/networkModule.test.js +1 -4
- package/tests/unit/unit.test.js +2 -2
- package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +2 -2
- package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +2 -2
- package/tests/unit/utils/protobuf/operationHelpers.test.js +4 -2
- package/tests/unit/utils/utils.test.js +0 -1
- package/proto/network/v1/enums/message_type.proto +0 -16
- package/proto/network/v1/enums/result_code.proto +0 -84
- package/proto/network/v1/messages/broadcast_transaction_request.proto +0 -9
- package/proto/network/v1/messages/broadcast_transaction_response.proto +0 -13
- package/proto/network/v1/messages/liveness_request.proto +0 -8
- package/proto/network/v1/messages/liveness_response.proto +0 -11
- package/proto/network/v1/network_message.proto +0 -22
- package/src/core/network/protocols/connectionPolicies.js +0 -88
- package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +0 -23
- package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +0 -27
- package/src/core/network/protocols/v1/V1ProtocolError.js +0 -91
- package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +0 -65
- package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +0 -389
- package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +0 -87
- package/src/core/network/protocols/v1/validators/V1BaseOperation.js +0 -211
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +0 -26
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +0 -276
- package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +0 -15
- package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +0 -17
- package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +0 -210
- package/src/core/network/services/PendingRequestService.js +0 -172
- package/src/core/network/services/TransactionCommitService.js +0 -149
- package/src/core/network/services/ValidatorHealthCheckService.js +0 -127
- package/src/utils/deepEqualApplyPayload.js +0 -40
- package/src/utils/logger.js +0 -25
- package/src/utils/protobuf/networkV1.generated.cjs +0 -2460
- package/tests/unit/network/LegacyNetworkMessageRouter.test.js +0 -54
- package/tests/unit/network/ProtocolSession.test.js +0 -127
- package/tests/unit/network/services/ConnectionManager.test.js +0 -450
- package/tests/unit/network/services/MessageOrchestrator.test.js +0 -445
- package/tests/unit/network/services/PendingRequestService.test.js +0 -431
- package/tests/unit/network/services/TransactionCommitService.test.js +0 -246
- package/tests/unit/network/services/TransactionPoolService.test.js +0 -489
- package/tests/unit/network/services/TransactionRateLimiterService.test.js +0 -139
- package/tests/unit/network/services/ValidatorHealthCheckService.test.js +0 -115
- package/tests/unit/network/services/services.test.js +0 -17
- package/tests/unit/network/utils/v1TestUtils.js +0 -153
- package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +0 -151
- package/tests/unit/network/v1/V1BaseOperation.test.js +0 -356
- package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +0 -129
- package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +0 -53
- package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +0 -512
- package/tests/unit/network/v1/V1LivenessRequest.test.js +0 -32
- package/tests/unit/network/v1/V1LivenessResponse.test.js +0 -45
- package/tests/unit/network/v1/V1ResultCode.test.js +0 -84
- package/tests/unit/network/v1/V1ValidationSchema.test.js +0 -13
- package/tests/unit/network/v1/connectionPolicies.test.js +0 -49
- package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +0 -284
- package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +0 -794
- package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +0 -193
- package/tests/unit/network/v1/v1.handlers.test.js +0 -15
- package/tests/unit/network/v1/v1.test.js +0 -19
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +0 -119
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +0 -136
- package/tests/unit/network/v1/v1ValidationSchema/common.test.js +0 -308
- package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +0 -90
- package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +0 -133
- package/tests/unit/utils/deepEqualApplyPayload/deepEqualApplyPayload.test.js +0 -102
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
import b4a from 'b4a';
|
|
2
|
-
|
|
3
|
-
export const describeValue = (value) => {
|
|
4
|
-
if (value === null) return 'null';
|
|
5
|
-
if (value === undefined) return 'undefined';
|
|
6
|
-
if (b4a.isBuffer(value)) return `buffer(len=${value.length})`;
|
|
7
|
-
if (Number.isNaN(value)) return 'NaN';
|
|
8
|
-
if (value === Infinity) return 'Infinity';
|
|
9
|
-
if (Array.isArray(value)) return 'array';
|
|
10
|
-
if (value instanceof Map) return 'map';
|
|
11
|
-
if (value instanceof Set) return 'set';
|
|
12
|
-
if (value instanceof Date) return 'date';
|
|
13
|
-
if (value instanceof RegExp) return 'regexp';
|
|
14
|
-
if (value instanceof Error) return `error(${value.message})`;
|
|
15
|
-
if (typeof value === 'bigint') return `bigint(${value})`;
|
|
16
|
-
return typeof value;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const assertNoThrowAndAbsent = (t, validateFn, input, message) => {
|
|
20
|
-
let result;
|
|
21
|
-
t.execution(() => {
|
|
22
|
-
result = validateFn(input);
|
|
23
|
-
}, `${message} (no throw)`);
|
|
24
|
-
t.absent(result, `${message} (should fail)`);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export function topLevelValidationTests(
|
|
28
|
-
t,
|
|
29
|
-
validateFn,
|
|
30
|
-
validFixture,
|
|
31
|
-
valueKey,
|
|
32
|
-
notAllowedDataTypes,
|
|
33
|
-
topFields,
|
|
34
|
-
expectedType
|
|
35
|
-
) {
|
|
36
|
-
t.test('strict mode', t => {
|
|
37
|
-
const extraTopLevel = {
|
|
38
|
-
...validFixture,
|
|
39
|
-
extra: b4a.from('redundant field', 'utf-8'),
|
|
40
|
-
};
|
|
41
|
-
t.absent(validateFn(extraTopLevel), 'Extra field should fail due to $$strict');
|
|
42
|
-
t.absent(validateFn({}), 'Empty object should fail');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
t.test('operation type', t => {
|
|
46
|
-
const invalidOperationType = { ...validFixture, type: 'invalid-op' };
|
|
47
|
-
t.absent(validateFn(invalidOperationType), 'Invalid operation type should fail');
|
|
48
|
-
|
|
49
|
-
const notDefinedOperationType = { ...validFixture, type: 999 };
|
|
50
|
-
t.absent(validateFn(notDefinedOperationType), 'Invalid operation type should fail');
|
|
51
|
-
|
|
52
|
-
const zeroValue = { ...validFixture, type: 0 };
|
|
53
|
-
t.absent(validateFn(zeroValue), 'Type with value 0 should fail');
|
|
54
|
-
|
|
55
|
-
const negativeValue = { ...validFixture, type: -1 };
|
|
56
|
-
t.absent(validateFn(negativeValue), 'Negative type value should fail');
|
|
57
|
-
|
|
58
|
-
const wrongKnownType = { ...validFixture, type: expectedType + 1 };
|
|
59
|
-
t.absent(validateFn(wrongKnownType), 'Wrong known type should fail');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
t.test('nested objects', t => {
|
|
63
|
-
const nestedObjectInsideValue = {
|
|
64
|
-
...validFixture,
|
|
65
|
-
[valueKey]: {
|
|
66
|
-
...validFixture[valueKey],
|
|
67
|
-
nested: { foo: b4a.from('bar', 'utf-8') }
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
t.absent(validateFn(nestedObjectInsideValue), `Unexpected nested field inside -${valueKey}- should fail due to strict`);
|
|
71
|
-
|
|
72
|
-
const nestedObjectInsideValue2 = {
|
|
73
|
-
...validFixture,
|
|
74
|
-
nested: { foo: b4a.from('bar', 'utf-8') }
|
|
75
|
-
};
|
|
76
|
-
t.absent(validateFn(nestedObjectInsideValue2), 'Unexpected nested field inside general object should fail due to strict');
|
|
77
|
-
|
|
78
|
-
const nestedObjectInsideType = {
|
|
79
|
-
...validFixture,
|
|
80
|
-
type: {
|
|
81
|
-
foo: b4a.from('bar', 'utf-8'),
|
|
82
|
-
nested: { foo: b4a.from('bar', 'utf-8') }
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
t.absent(validateFn(nestedObjectInsideType), 'Unexpected nested field inside `type` field should fail');
|
|
86
|
-
|
|
87
|
-
const nestedObjectInsideId = {
|
|
88
|
-
...validFixture,
|
|
89
|
-
id: {
|
|
90
|
-
foo: b4a.from('bar', 'utf-8'),
|
|
91
|
-
nested: { foo: b4a.from('bar', 'utf-8') }
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
t.absent(validateFn(nestedObjectInsideId), 'Unexpected nested field inside `id` field should fail');
|
|
95
|
-
|
|
96
|
-
const nestedObjectInsideTimestamp = {
|
|
97
|
-
...validFixture,
|
|
98
|
-
timestamp: {
|
|
99
|
-
foo: b4a.from('bar', 'utf-8'),
|
|
100
|
-
nested: { foo: b4a.from('bar', 'utf-8') }
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
t.absent(validateFn(nestedObjectInsideTimestamp), 'Unexpected nested field inside `timestamp` field should fail');
|
|
104
|
-
|
|
105
|
-
const nestedObjectInsideCapabilities = {
|
|
106
|
-
...validFixture,
|
|
107
|
-
capabilities: {
|
|
108
|
-
foo: b4a.from('bar', 'utf-8'),
|
|
109
|
-
nested: { foo: b4a.from('bar', 'utf-8') }
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
t.absent(validateFn(nestedObjectInsideCapabilities), 'Unexpected nested field inside `capabilities` field should fail');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
t.test('invalid data types', t => {
|
|
116
|
-
for (const invalidDataType of notAllowedDataTypes) {
|
|
117
|
-
if (typeof invalidDataType !== 'number') {
|
|
118
|
-
const invalidTypeForType = { ...validFixture, type: invalidDataType };
|
|
119
|
-
t.absent(
|
|
120
|
-
validateFn(invalidTypeForType),
|
|
121
|
-
`Invalid data type for 'type' key ${describeValue(invalidDataType)} should fail`
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (typeof invalidDataType !== 'string') {
|
|
126
|
-
const invalidTypeForId = { ...validFixture, id: invalidDataType };
|
|
127
|
-
t.absent(
|
|
128
|
-
validateFn(invalidTypeForId),
|
|
129
|
-
`Invalid data type for 'id' key ${describeValue(invalidDataType)} should fail`
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (typeof invalidDataType !== 'number') {
|
|
134
|
-
const invalidTypeForTimestamp = { ...validFixture, timestamp: invalidDataType };
|
|
135
|
-
t.absent(
|
|
136
|
-
validateFn(invalidTypeForTimestamp),
|
|
137
|
-
`Invalid data type for 'timestamp' key ${describeValue(invalidDataType)} should fail`
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (!Array.isArray(invalidDataType)) {
|
|
142
|
-
const invalidTypeForCapabilities = { ...validFixture, capabilities: invalidDataType };
|
|
143
|
-
t.absent(
|
|
144
|
-
validateFn(invalidTypeForCapabilities),
|
|
145
|
-
`Invalid data type for 'capabilities' key ${describeValue(invalidDataType)} should fail`
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (String(invalidDataType) !== '[object Object]') {
|
|
150
|
-
const invalidTypeForPayload = { ...validFixture, [valueKey]: invalidDataType };
|
|
151
|
-
t.absent(
|
|
152
|
-
validateFn(invalidTypeForPayload),
|
|
153
|
-
`Invalid data type for ${valueKey} key ${describeValue(invalidDataType)} should fail`
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const invalidOperationTypeDiffType = { ...validFixture, type: "string" }
|
|
159
|
-
t.absent(validateFn(invalidOperationTypeDiffType), 'Wrong type for `type` should fail')
|
|
160
|
-
|
|
161
|
-
for (const mainField of topFields) {
|
|
162
|
-
const missingFieldInvalidInput = { ...validFixture }
|
|
163
|
-
delete missingFieldInvalidInput[mainField]
|
|
164
|
-
t.absent(validateFn(missingFieldInvalidInput), `Missing ${mainField} should fail`);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function headerFieldValueValidationTests(t, validateFn, validFixture) {
|
|
170
|
-
t.absent(
|
|
171
|
-
validateFn({...validFixture, id: ''}),
|
|
172
|
-
'empty id should fail'
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
t.absent(
|
|
176
|
-
validateFn({...validFixture, id: 'x'.repeat(65)}),
|
|
177
|
-
'id longer than 64 chars should fail'
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
t.absent(
|
|
181
|
-
validateFn({...validFixture, timestamp: 0}),
|
|
182
|
-
'timestamp 0 should fail'
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
t.absent(
|
|
186
|
-
validateFn({...validFixture, timestamp: -1}),
|
|
187
|
-
'negative timestamp should fail'
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
t.absent(
|
|
191
|
-
validateFn({...validFixture, timestamp: 1.1}),
|
|
192
|
-
'non-integer timestamp should fail'
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
t.absent(
|
|
196
|
-
validateFn({...validFixture, timestamp: NaN}),
|
|
197
|
-
'timestamp NaN should fail'
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
t.absent(
|
|
201
|
-
validateFn({...validFixture, timestamp: Infinity}),
|
|
202
|
-
'timestamp Infinity should fail'
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
t.absent(
|
|
206
|
-
validateFn({...validFixture, timestamp: Number.MAX_SAFE_INTEGER + 1}),
|
|
207
|
-
'timestamp above MAX_SAFE_INTEGER should fail'
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
t.absent(
|
|
211
|
-
validateFn({...validFixture, capabilities: 'cap:a'}),
|
|
212
|
-
'capabilities must be an array'
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
t.absent(
|
|
216
|
-
validateFn({...validFixture, capabilities: [1]}),
|
|
217
|
-
'capabilities items must be strings'
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export function valueLevelValidationTests(
|
|
222
|
-
t,
|
|
223
|
-
validateFn,
|
|
224
|
-
validFixture,
|
|
225
|
-
valueKey,
|
|
226
|
-
valueFields,
|
|
227
|
-
notAllowedDataTypes,
|
|
228
|
-
opts = {}
|
|
229
|
-
) {
|
|
230
|
-
const skipInvalidType = opts.skipInvalidType || (() => false);
|
|
231
|
-
|
|
232
|
-
for (const field of valueFields) {
|
|
233
|
-
const missing = structuredClone(validFixture);
|
|
234
|
-
delete missing[valueKey][field];
|
|
235
|
-
t.absent(validateFn(missing), `Missing ${valueKey}.${field} should fail`);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
t.test(`Invalid data types for each field in ${valueKey}`, t => {
|
|
239
|
-
for (const field of valueFields) {
|
|
240
|
-
for (const invalidType of notAllowedDataTypes) {
|
|
241
|
-
if (skipInvalidType(field, invalidType)) continue;
|
|
242
|
-
|
|
243
|
-
const withInvalidDataType = structuredClone(validFixture);
|
|
244
|
-
withInvalidDataType[valueKey][field] = invalidType;
|
|
245
|
-
t.absent(
|
|
246
|
-
validateFn(withInvalidDataType),
|
|
247
|
-
`Invalid data type for ${valueKey}.${field}: ${describeValue(invalidType)} should fail`
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
t.test("Extra field in value", t => {
|
|
254
|
-
const extraInValue = structuredClone(validFixture);
|
|
255
|
-
extraInValue[valueKey].extraField = b4a.from('redundant', 'utf-8');
|
|
256
|
-
t.absent(validateFn(extraInValue), 'Extra field should fail due to strict')
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export function fieldsBufferLengthTest(
|
|
261
|
-
t,
|
|
262
|
-
validateFn,
|
|
263
|
-
validFixture,
|
|
264
|
-
valueKey,
|
|
265
|
-
requiredFieldLengthsForValue
|
|
266
|
-
) {
|
|
267
|
-
for (const [field, expectedLen] of Object.entries(requiredFieldLengthsForValue)) {
|
|
268
|
-
const emptyBuffer = b4a.alloc(0);
|
|
269
|
-
const oneTooShort = b4a.alloc(expectedLen - 1, 0x01);
|
|
270
|
-
const tooShort = b4a.alloc(expectedLen - 2, 0x01);
|
|
271
|
-
const exact = b4a.alloc(expectedLen, 0x01);
|
|
272
|
-
const oneTooLong = b4a.alloc(expectedLen + 1, 0x01);
|
|
273
|
-
const tooLong = b4a.alloc(expectedLen + 2, 0x01);
|
|
274
|
-
|
|
275
|
-
const buildValueLevel = (val) => ({
|
|
276
|
-
...validFixture,
|
|
277
|
-
[valueKey]: {
|
|
278
|
-
...validFixture[valueKey],
|
|
279
|
-
[field]: val
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const inputs = {
|
|
284
|
-
emptyBufferInput: buildValueLevel(emptyBuffer),
|
|
285
|
-
shortInput: buildValueLevel(tooShort),
|
|
286
|
-
oneTooShortInput: buildValueLevel(oneTooShort),
|
|
287
|
-
exactInput: buildValueLevel(exact),
|
|
288
|
-
oneTooLongInput: buildValueLevel(oneTooLong),
|
|
289
|
-
longInput: buildValueLevel(tooLong),
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
t.absent(validateFn(inputs.emptyBufferInput), `${valueKey}.${field} empty buffer (length ${emptyBuffer.length}) should fail`);
|
|
293
|
-
t.absent(validateFn(inputs.shortInput), `${valueKey}.${field} too short (length ${tooShort.length}) should fail`);
|
|
294
|
-
t.absent(validateFn(inputs.oneTooShortInput), `${valueKey}.${field} one too short (length ${oneTooShort.length}) should fail`);
|
|
295
|
-
t.ok(validateFn(inputs.exactInput), `${valueKey}.${field} exact length (length ${exact.length}) should pass`);
|
|
296
|
-
t.absent(validateFn(inputs.oneTooLongInput), `${valueKey}.${field} one too long (length ${oneTooLong.length}) should fail`);
|
|
297
|
-
t.absent(validateFn(inputs.longInput), `${valueKey}.${field} too long (length ${tooLong.length}) should fail`);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
export function fieldsNonZeroBufferTest(t, validateFn, validFixture, valueKey, fields) {
|
|
302
|
-
for (const field of fields) {
|
|
303
|
-
const zeroFilled = structuredClone(validFixture);
|
|
304
|
-
const len = zeroFilled[valueKey][field]?.length ?? 0;
|
|
305
|
-
zeroFilled[valueKey][field] = b4a.alloc(len, 0);
|
|
306
|
-
t.absent(validateFn(zeroFilled), `${valueKey}.${field} all-zero buffer should fail`);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import test from 'brittle';
|
|
2
|
-
import b4a from 'b4a';
|
|
3
|
-
|
|
4
|
-
import V1ValidationSchema from '../../../../../src/core/network/protocols/v1/validators/V1ValidationSchema.js';
|
|
5
|
-
import {
|
|
6
|
-
NetworkOperationType,
|
|
7
|
-
NONCE_BYTE_LENGTH,
|
|
8
|
-
SIGNATURE_BYTE_LENGTH,
|
|
9
|
-
} from '../../../../../src/utils/constants.js';
|
|
10
|
-
import {not_allowed_data_types} from '../../../../fixtures/check.fixtures.js';
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
fieldsBufferLengthTest,
|
|
14
|
-
fieldsNonZeroBufferTest,
|
|
15
|
-
headerFieldValueValidationTests,
|
|
16
|
-
topLevelValidationTests,
|
|
17
|
-
valueLevelValidationTests,
|
|
18
|
-
} from './common.test.js';
|
|
19
|
-
|
|
20
|
-
const v = new V1ValidationSchema();
|
|
21
|
-
|
|
22
|
-
const validFixture = {
|
|
23
|
-
type: NetworkOperationType.LIVENESS_REQUEST,
|
|
24
|
-
id: 'test-id',
|
|
25
|
-
timestamp: Date.now(),
|
|
26
|
-
liveness_request: {
|
|
27
|
-
nonce: b4a.alloc(NONCE_BYTE_LENGTH, 1),
|
|
28
|
-
signature: b4a.alloc(SIGNATURE_BYTE_LENGTH, 2),
|
|
29
|
-
},
|
|
30
|
-
capabilities: ['cap:a', 'cap:b'],
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const topFields = ['type', 'id', 'timestamp', 'liveness_request', 'capabilities'];
|
|
34
|
-
const valueFields = ['nonce', 'signature'];
|
|
35
|
-
const requiredLengths = {
|
|
36
|
-
nonce: NONCE_BYTE_LENGTH,
|
|
37
|
-
signature: SIGNATURE_BYTE_LENGTH,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
test('V1ValidationSchema.validateV1LivenessRequest - happy path', t => {
|
|
41
|
-
t.ok(v.validateV1LivenessRequest(validFixture), 'valid LIVENESS_REQUEST should pass');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('V1ValidationSchema.validateV1LivenessRequest - top level validation', t => {
|
|
45
|
-
topLevelValidationTests(
|
|
46
|
-
t,
|
|
47
|
-
v.validateV1LivenessRequest.bind(v),
|
|
48
|
-
validFixture,
|
|
49
|
-
'liveness_request',
|
|
50
|
-
not_allowed_data_types,
|
|
51
|
-
topFields,
|
|
52
|
-
NetworkOperationType.LIVENESS_REQUEST
|
|
53
|
-
);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('V1ValidationSchema.validateV1LivenessRequest - header field values', t => {
|
|
57
|
-
headerFieldValueValidationTests(t, v.validateV1LivenessRequest.bind(v), validFixture);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('V1ValidationSchema.validateV1LivenessRequest - payload validation', t => {
|
|
61
|
-
valueLevelValidationTests(
|
|
62
|
-
t,
|
|
63
|
-
v.validateV1LivenessRequest.bind(v),
|
|
64
|
-
validFixture,
|
|
65
|
-
'liveness_request',
|
|
66
|
-
valueFields,
|
|
67
|
-
not_allowed_data_types,
|
|
68
|
-
);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('V1ValidationSchema.validateV1LivenessRequest - buffer lengths', t => {
|
|
72
|
-
fieldsBufferLengthTest(
|
|
73
|
-
t,
|
|
74
|
-
v.validateV1LivenessRequest.bind(v),
|
|
75
|
-
validFixture,
|
|
76
|
-
'liveness_request',
|
|
77
|
-
requiredLengths
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('V1ValidationSchema.validateV1LivenessRequest - non-zero buffers', t => {
|
|
82
|
-
fieldsNonZeroBufferTest(
|
|
83
|
-
t,
|
|
84
|
-
v.validateV1LivenessRequest.bind(v),
|
|
85
|
-
validFixture,
|
|
86
|
-
'liveness_request',
|
|
87
|
-
['nonce', 'signature']
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import test from 'brittle';
|
|
2
|
-
import b4a from 'b4a';
|
|
3
|
-
|
|
4
|
-
import V1ValidationSchema from '../../../../../src/core/network/protocols/v1/validators/V1ValidationSchema.js';
|
|
5
|
-
import {
|
|
6
|
-
NetworkOperationType,
|
|
7
|
-
NONCE_BYTE_LENGTH,
|
|
8
|
-
ResultCode,
|
|
9
|
-
SIGNATURE_BYTE_LENGTH,
|
|
10
|
-
} from '../../../../../src/utils/constants.js';
|
|
11
|
-
import {not_allowed_data_types} from '../../../../fixtures/check.fixtures.js';
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
assertNoThrowAndAbsent,
|
|
15
|
-
fieldsBufferLengthTest,
|
|
16
|
-
fieldsNonZeroBufferTest,
|
|
17
|
-
headerFieldValueValidationTests,
|
|
18
|
-
topLevelValidationTests,
|
|
19
|
-
valueLevelValidationTests,
|
|
20
|
-
} from './common.test.js';
|
|
21
|
-
|
|
22
|
-
const v = new V1ValidationSchema();
|
|
23
|
-
|
|
24
|
-
const validFixture = {
|
|
25
|
-
type: NetworkOperationType.LIVENESS_RESPONSE,
|
|
26
|
-
id: 'test-id',
|
|
27
|
-
timestamp: Date.now(),
|
|
28
|
-
liveness_response: {
|
|
29
|
-
nonce: b4a.alloc(NONCE_BYTE_LENGTH, 1),
|
|
30
|
-
signature: b4a.alloc(SIGNATURE_BYTE_LENGTH, 2),
|
|
31
|
-
result: ResultCode.OK,
|
|
32
|
-
},
|
|
33
|
-
capabilities: ['cap:a'],
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const topFields = ['type', 'id', 'timestamp', 'liveness_response', 'capabilities'];
|
|
37
|
-
const valueFields = ['nonce', 'signature', 'result'];
|
|
38
|
-
const requiredLengths = {
|
|
39
|
-
nonce: NONCE_BYTE_LENGTH,
|
|
40
|
-
signature: SIGNATURE_BYTE_LENGTH,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
test('V1ValidationSchema.validateV1LivenessResponse - happy path', t => {
|
|
44
|
-
t.ok(v.validateV1LivenessResponse(validFixture), 'valid LIVENESS_RESPONSE should pass');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('V1ValidationSchema.validateV1LivenessResponse - payload/type mismatch (request payload + response type)', t => {
|
|
48
|
-
const op = structuredClone(validFixture);
|
|
49
|
-
delete op.liveness_response;
|
|
50
|
-
op.liveness_request = {
|
|
51
|
-
nonce: b4a.alloc(NONCE_BYTE_LENGTH, 1),
|
|
52
|
-
signature: b4a.alloc(SIGNATURE_BYTE_LENGTH, 2),
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
assertNoThrowAndAbsent(
|
|
56
|
-
t,
|
|
57
|
-
v.validateV1LivenessResponse.bind(v),
|
|
58
|
-
op,
|
|
59
|
-
'liveness_request payload with type=LIVENESS_RESPONSE'
|
|
60
|
-
);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('V1ValidationSchema.validateV1LivenessResponse - top level validation', t => {
|
|
64
|
-
topLevelValidationTests(
|
|
65
|
-
t,
|
|
66
|
-
v.validateV1LivenessResponse.bind(v),
|
|
67
|
-
validFixture,
|
|
68
|
-
'liveness_response',
|
|
69
|
-
not_allowed_data_types,
|
|
70
|
-
topFields,
|
|
71
|
-
NetworkOperationType.LIVENESS_RESPONSE
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('V1ValidationSchema.validateV1LivenessResponse - header field values', t => {
|
|
76
|
-
headerFieldValueValidationTests(t, v.validateV1LivenessResponse.bind(v), validFixture);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('V1ValidationSchema.validateV1LivenessResponse - payload validation', t => {
|
|
80
|
-
valueLevelValidationTests(
|
|
81
|
-
t,
|
|
82
|
-
v.validateV1LivenessResponse.bind(v),
|
|
83
|
-
validFixture,
|
|
84
|
-
'liveness_response',
|
|
85
|
-
valueFields,
|
|
86
|
-
not_allowed_data_types,
|
|
87
|
-
{
|
|
88
|
-
skipInvalidType: (field, invalidType) => field === 'result' && typeof invalidType === 'number',
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('V1ValidationSchema.validateV1LivenessResponse - result value validation', t => {
|
|
94
|
-
const negative = structuredClone(validFixture);
|
|
95
|
-
negative.liveness_response.result = -1;
|
|
96
|
-
t.absent(v.validateV1LivenessResponse(negative), 'negative result should fail');
|
|
97
|
-
|
|
98
|
-
const nonInteger = structuredClone(validFixture);
|
|
99
|
-
nonInteger.liveness_response.result = 1.1;
|
|
100
|
-
t.absent(v.validateV1LivenessResponse(nonInteger), 'non-integer result should fail');
|
|
101
|
-
|
|
102
|
-
const nan = structuredClone(validFixture);
|
|
103
|
-
nan.liveness_response.result = NaN;
|
|
104
|
-
t.absent(v.validateV1LivenessResponse(nan), 'result NaN should fail');
|
|
105
|
-
|
|
106
|
-
const infinity = structuredClone(validFixture);
|
|
107
|
-
infinity.liveness_response.result = Infinity;
|
|
108
|
-
t.absent(v.validateV1LivenessResponse(infinity), 'result Infinity should fail');
|
|
109
|
-
|
|
110
|
-
const unknownCode = structuredClone(validFixture);
|
|
111
|
-
unknownCode.liveness_response.result = Math.max(...Object.values(ResultCode)) + 1;
|
|
112
|
-
t.absent(v.validateV1LivenessResponse(unknownCode), 'unknown result code should fail');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test('V1ValidationSchema.validateV1LivenessResponse - buffer lengths', t => {
|
|
116
|
-
fieldsBufferLengthTest(
|
|
117
|
-
t,
|
|
118
|
-
v.validateV1LivenessResponse.bind(v),
|
|
119
|
-
validFixture,
|
|
120
|
-
'liveness_response',
|
|
121
|
-
requiredLengths
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test('V1ValidationSchema.validateV1LivenessResponse - non-zero buffers', t => {
|
|
126
|
-
fieldsNonZeroBufferTest(
|
|
127
|
-
t,
|
|
128
|
-
v.validateV1LivenessResponse.bind(v),
|
|
129
|
-
validFixture,
|
|
130
|
-
'liveness_response',
|
|
131
|
-
['nonce', 'signature']
|
|
132
|
-
);
|
|
133
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import test from 'brittle';
|
|
2
|
-
import b4a from 'b4a';
|
|
3
|
-
|
|
4
|
-
import fixtures from '../../../fixtures/protobuf.fixtures.js';
|
|
5
|
-
import { isDeepEqualApplyPayload } from '../../../../src/utils/deepEqualApplyPayload.js';
|
|
6
|
-
|
|
7
|
-
const applyPayloads = new Map([
|
|
8
|
-
['txComplete', fixtures.validTransactionOperation],
|
|
9
|
-
['txPartial', fixtures.validPartialTransactionOperation],
|
|
10
|
-
['addIndexer', fixtures.validAddIndexer],
|
|
11
|
-
['removeIndexer', fixtures.validRemoveIndexer],
|
|
12
|
-
['appendWhitelist', fixtures.validAppendWhitelist],
|
|
13
|
-
['banValidator', fixtures.validBanValidator],
|
|
14
|
-
['addAdmin', fixtures.validAddAdmin],
|
|
15
|
-
['addWriterComplete', fixtures.validCompleteAddWriter],
|
|
16
|
-
['addWriterPartial', fixtures.validPartialAddWriter],
|
|
17
|
-
['removeWriterComplete', fixtures.validCompleteRemoveWriter],
|
|
18
|
-
['removeWriterPartial', fixtures.validPartialRemoveWriter],
|
|
19
|
-
['adminRecoveryComplete', fixtures.validCompleteAdminRecovery],
|
|
20
|
-
['adminRecoveryPartial', fixtures.validPartialAdminRecovery],
|
|
21
|
-
['bootstrapDeploymentComplete', fixtures.validCompleteBootstrapDeployment],
|
|
22
|
-
['bootstrapDeploymentPartial', fixtures.validPartialBootstrapDeployment],
|
|
23
|
-
['transferComplete', fixtures.validTransferOperation],
|
|
24
|
-
['transferPartial', fixtures.validPartialTransferOperation],
|
|
25
|
-
['balanceInitialization', fixtures.validBalanceInitOperation],
|
|
26
|
-
['disableInitialization', fixtures.validDisableInitialization],
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
const cloneDeep = (value) => {
|
|
30
|
-
if (b4a.isBuffer(value)) return b4a.from(value);
|
|
31
|
-
|
|
32
|
-
if (Array.isArray(value)) {
|
|
33
|
-
return value.map(cloneDeep);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (value && typeof value === 'object') {
|
|
37
|
-
const clone = {};
|
|
38
|
-
for (const key of Object.keys(value)) {
|
|
39
|
-
clone[key] = cloneDeep(value[key]);
|
|
40
|
-
}
|
|
41
|
-
return clone;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return value;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const mutateFirstBuffer = (value) => {
|
|
48
|
-
if (!value || typeof value !== 'object') return false;
|
|
49
|
-
|
|
50
|
-
if (Array.isArray(value)) {
|
|
51
|
-
for (let i = 0; i < value.length; i++) {
|
|
52
|
-
if (mutateFirstBuffer(value[i])) return true;
|
|
53
|
-
}
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
for (const key of Object.keys(value)) {
|
|
58
|
-
const current = value[key];
|
|
59
|
-
|
|
60
|
-
if (b4a.isBuffer(current)) {
|
|
61
|
-
if (current.length === 0) {
|
|
62
|
-
value[key] = b4a.from([0x01]);
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
current[0] = current[0] ^ 0xff;
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (current && typeof current === 'object') {
|
|
71
|
-
if (mutateFirstBuffer(current)) return true;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return false;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
test('isDeepEqualApplyPayload returns true for deep-equal apply payload fixtures', t => {
|
|
79
|
-
for (const [name, payload] of applyPayloads) {
|
|
80
|
-
const cloned = cloneDeep(payload);
|
|
81
|
-
t.ok(isDeepEqualApplyPayload(payload, cloned), `${name} should be deep-equal`);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test('isDeepEqualApplyPayload returns false when payload content differs', t => {
|
|
86
|
-
for (const [name, payload] of applyPayloads) {
|
|
87
|
-
const mutated = cloneDeep(payload);
|
|
88
|
-
const didMutate = mutateFirstBuffer(mutated);
|
|
89
|
-
|
|
90
|
-
t.ok(didMutate, `${name} mutation should modify at least one buffer`);
|
|
91
|
-
t.absent(isDeepEqualApplyPayload(payload, mutated), `${name} should not be deep-equal after mutation`);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('isDeepEqualApplyPayload handles primitive and structural mismatches', t => {
|
|
96
|
-
t.ok(isDeepEqualApplyPayload(null, null), 'null equals null');
|
|
97
|
-
t.absent(isDeepEqualApplyPayload(null, undefined), 'null and undefined differ');
|
|
98
|
-
t.absent(isDeepEqualApplyPayload(1, '1'), 'number and string differ');
|
|
99
|
-
t.absent(isDeepEqualApplyPayload([], {}), 'array and object differ');
|
|
100
|
-
t.absent(isDeepEqualApplyPayload({ type: 1 }, { type: 1, extra: true }), 'missing/extra keys differ');
|
|
101
|
-
});
|
|
102
|
-
|