trac-msb 0.2.12 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -4
- package/proto/network/v1/enums/message_type.proto +16 -0
- package/proto/network/v1/enums/result_code.proto +84 -0
- package/proto/network/v1/messages/broadcast_transaction_request.proto +9 -0
- package/proto/network/v1/messages/broadcast_transaction_response.proto +13 -0
- package/proto/network/v1/messages/liveness_request.proto +8 -0
- package/proto/network/v1/messages/liveness_response.proto +11 -0
- package/proto/network/v1/network_message.proto +22 -0
- package/rpc/rpc_services.js +22 -4
- package/scripts/generate-protobufs.js +37 -12
- package/src/config/config.js +26 -5
- package/src/config/env.js +25 -11
- package/src/core/network/Network.js +73 -36
- package/src/core/network/protocols/LegacyProtocol.js +21 -11
- package/src/core/network/protocols/NetworkMessages.js +38 -17
- package/src/core/network/protocols/ProtocolInterface.js +14 -2
- package/src/core/network/protocols/ProtocolSession.js +144 -17
- package/src/core/network/protocols/V1Protocol.js +37 -18
- package/src/core/network/protocols/connectionPolicies.js +88 -0
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +25 -19
- package/src/core/network/protocols/{shared/handlers/base/BaseOperationHandler.js → legacy/handlers/BaseStateOperationHandler.js} +23 -12
- package/src/core/network/protocols/legacy/handlers/{GetRequestHandler.js → LegacyGetRequestHandler.js} +6 -6
- package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +23 -0
- package/src/core/network/protocols/{shared/handlers/RoleOperationHandler.js → legacy/handlers/LegacyRoleOperationHandler.js} +18 -11
- package/src/core/network/protocols/{shared/handlers/SubnetworkOperationHandler.js → legacy/handlers/LegacySubnetworkOperationHandler.js} +28 -17
- package/src/core/network/protocols/{shared/handlers/TransferOperationHandler.js → legacy/handlers/LegacyTransferOperationHandler.js} +17 -11
- package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +27 -0
- package/src/core/network/protocols/shared/validators/{PartialBootstrapDeployment.js → PartialBootstrapDeploymentValidator.js} +9 -4
- package/src/core/network/protocols/shared/validators/{base/PartialOperation.js → PartialOperationValidator.js} +47 -25
- package/src/core/network/protocols/shared/validators/{PartialRoleAccess.js → PartialRoleAccessValidator.js} +51 -17
- package/src/core/network/protocols/shared/validators/{PartialTransaction.js → PartialTransactionValidator.js} +21 -7
- package/src/core/network/protocols/shared/validators/{PartialTransfer.js → PartialTransferValidator.js} +26 -9
- package/src/core/network/protocols/v1/NetworkMessageRouter.js +91 -7
- package/src/core/network/protocols/v1/V1ProtocolError.js +91 -0
- package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +65 -0
- package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +389 -0
- package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +87 -0
- package/src/core/network/protocols/v1/validators/V1BaseOperation.js +211 -0
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +26 -0
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +276 -0
- package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +15 -0
- package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +17 -0
- package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +210 -0
- package/src/core/network/services/ConnectionManager.js +146 -94
- package/src/core/network/services/MessageOrchestrator.js +151 -27
- package/src/core/network/services/PendingRequestService.js +172 -0
- package/src/core/network/services/TransactionCommitService.js +149 -0
- package/src/core/network/services/TransactionPoolService.js +129 -18
- package/src/core/network/services/TransactionRateLimiterService.js +52 -34
- package/src/core/network/services/ValidatorHealthCheckService.js +127 -0
- package/src/core/network/services/ValidatorObserverService.js +18 -26
- package/src/core/state/State.js +70 -19
- package/src/index.js +5 -4
- package/src/messages/network/v1/NetworkMessageBuilder.js +59 -79
- package/src/messages/network/v1/NetworkMessageDirector.js +16 -50
- package/src/utils/Scheduler.js +0 -8
- package/src/utils/constants.js +71 -5
- package/src/utils/deepEqualApplyPayload.js +40 -0
- package/src/utils/helpers.js +10 -1
- package/src/utils/logger.js +25 -0
- package/src/utils/normalizers.js +38 -0
- package/src/utils/protobuf/networkV1.generated.cjs +2460 -0
- package/src/utils/protobuf/operationHelpers.js +24 -3
- package/tests/acceptance/v1/account/account.test.mjs +8 -2
- package/tests/acceptance/v1/tx/tx.test.mjs +23 -1
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +34 -6
- package/tests/fixtures/networkV1.fixtures.js +2 -28
- package/tests/helpers/transactionPayloads.mjs +2 -2
- package/tests/unit/messages/network/NetworkMessageBuilder.test.js +239 -79
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +223 -77
- package/tests/unit/network/LegacyNetworkMessageRouter.test.js +54 -0
- package/tests/unit/network/ProtocolSession.test.js +127 -0
- package/tests/unit/network/networkModule.test.js +4 -1
- package/tests/unit/network/services/ConnectionManager.test.js +450 -0
- package/tests/unit/network/services/MessageOrchestrator.test.js +445 -0
- package/tests/unit/network/services/PendingRequestService.test.js +431 -0
- package/tests/unit/network/services/TransactionCommitService.test.js +246 -0
- package/tests/unit/network/services/TransactionPoolService.test.js +489 -0
- package/tests/unit/network/services/TransactionRateLimiterService.test.js +139 -0
- package/tests/unit/network/services/ValidatorHealthCheckService.test.js +115 -0
- package/tests/unit/network/services/services.test.js +17 -0
- package/tests/unit/network/utils/v1TestUtils.js +153 -0
- package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +151 -0
- package/tests/unit/network/v1/V1BaseOperation.test.js +356 -0
- package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +129 -0
- package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +53 -0
- package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +512 -0
- package/tests/unit/network/v1/V1LivenessRequest.test.js +32 -0
- package/tests/unit/network/v1/V1LivenessResponse.test.js +45 -0
- package/tests/unit/network/v1/V1ResultCode.test.js +84 -0
- package/tests/unit/network/v1/V1ValidationSchema.test.js +13 -0
- package/tests/unit/network/v1/connectionPolicies.test.js +49 -0
- package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +284 -0
- package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +794 -0
- package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +193 -0
- package/tests/unit/network/v1/v1.handlers.test.js +15 -0
- package/tests/unit/network/v1/v1.test.js +19 -0
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +119 -0
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +136 -0
- package/tests/unit/network/v1/v1ValidationSchema/common.test.js +308 -0
- package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +90 -0
- package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +133 -0
- package/tests/unit/unit.test.js +2 -2
- package/tests/unit/utils/deepEqualApplyPayload/deepEqualApplyPayload.test.js +102 -0
- package/tests/unit/utils/protobuf/operationHelpers.test.js +2 -4
- package/tests/unit/utils/utils.test.js +1 -0
- package/.github/workflows/acceptance-tests.yml +0 -38
- package/.github/workflows/lint-pr-title.yml +0 -26
- package/.github/workflows/publish.yml +0 -33
- package/.github/workflows/unit-tests.yml +0 -34
- package/proto/network.proto +0 -74
- package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +0 -37
- package/src/utils/protobuf/network.cjs +0 -840
- package/tests/unit/network/ConnectionManager.test.js +0 -191
|
@@ -13,10 +13,11 @@ import {
|
|
|
13
13
|
idToBuffer,
|
|
14
14
|
timestampToBuffer
|
|
15
15
|
} from '../../../../src/utils/buffer.js';
|
|
16
|
-
import { addressToBuffer } from '../../../../src/core/state/utils/address.js';
|
|
17
16
|
import { config } from '../../../helpers/config.js';
|
|
18
17
|
import { asAddress } from '../../../helpers/address.js';
|
|
19
18
|
import { testKeyPair1 } from '../../../fixtures/apply.fixtures.js';
|
|
19
|
+
import { v7 as uuidv7 } from 'uuid';
|
|
20
|
+
import { errorMessageIncludes } from '../../../helpers/regexHelper.js';
|
|
20
21
|
|
|
21
22
|
function createWallet() {
|
|
22
23
|
const keyPair = {
|
|
@@ -34,64 +35,15 @@ function uniqueResultCodes() {
|
|
|
34
35
|
return [...new Set(Object.values(NetworkResultCode))].sort((a, b) => a - b);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
test('NetworkMessageDirector builds validator connection request and verifies signature', async t => {
|
|
38
|
-
const wallet = createWallet();
|
|
39
|
-
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
40
|
-
|
|
41
|
-
const id = '1';
|
|
42
|
-
const caps = ['cap:b', 'cap:a'];
|
|
43
|
-
|
|
44
|
-
const payload = await director.buildValidatorConnectionRequest(id, wallet.address, caps);
|
|
45
|
-
t.is(payload.type, NetworkOperationType.VALIDATOR_CONNECTION_REQUEST);
|
|
46
|
-
t.is(payload.id, id);
|
|
47
|
-
t.alike(payload.capabilities, caps);
|
|
48
|
-
|
|
49
|
-
const msg = createMessage(
|
|
50
|
-
payload.type,
|
|
51
|
-
idToBuffer(payload.id),
|
|
52
|
-
timestampToBuffer(payload.timestamp),
|
|
53
|
-
addressToBuffer(wallet.address, config.addressPrefix),
|
|
54
|
-
payload.validator_connection_request.nonce,
|
|
55
|
-
encodeCapabilities(caps)
|
|
56
|
-
);
|
|
57
|
-
const hash = await PeerWallet.blake3(msg);
|
|
58
|
-
t.ok(wallet.verify(payload.validator_connection_request.signature, hash, wallet.publicKey));
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('NetworkMessageDirector builds liveness request and verifies signature', async t => {
|
|
62
|
-
const wallet = createWallet();
|
|
63
|
-
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
64
|
-
|
|
65
|
-
const id = '1';
|
|
66
|
-
const caps = ['cap:b', 'cap:a'];
|
|
67
|
-
const data = b4a.from('ping', 'utf8');
|
|
68
|
-
|
|
69
|
-
const payload = await director.buildLivenessRequest(id, data, caps);
|
|
70
|
-
t.is(payload.type, NetworkOperationType.LIVENESS_REQUEST);
|
|
71
|
-
t.is(payload.id, id);
|
|
72
|
-
t.alike(payload.capabilities, caps);
|
|
73
|
-
|
|
74
|
-
const msg = createMessage(
|
|
75
|
-
payload.type,
|
|
76
|
-
idToBuffer(payload.id),
|
|
77
|
-
timestampToBuffer(payload.timestamp),
|
|
78
|
-
payload.liveness_request.nonce,
|
|
79
|
-
encodeCapabilities(caps)
|
|
80
|
-
);
|
|
81
|
-
const hash = await PeerWallet.blake3(msg);
|
|
82
|
-
t.ok(wallet.verify(payload.liveness_request.signature, hash, wallet.publicKey));
|
|
83
|
-
});
|
|
84
|
-
|
|
85
38
|
test('NetworkMessageDirector iterates liveness response ResultCode values', async t => {
|
|
86
39
|
const wallet = createWallet();
|
|
87
40
|
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
88
41
|
|
|
89
|
-
const id =
|
|
42
|
+
const id = uuidv7();
|
|
90
43
|
const caps = ['cap:b', 'cap:a'];
|
|
91
|
-
const data = b4a.from('ping', 'utf8');
|
|
92
44
|
|
|
93
45
|
for (const code of uniqueResultCodes()) {
|
|
94
|
-
const payload = await director.buildLivenessResponse(id,
|
|
46
|
+
const payload = await director.buildLivenessResponse(id, caps, code);
|
|
95
47
|
t.is(payload.type, NetworkOperationType.LIVENESS_RESPONSE);
|
|
96
48
|
t.is(payload.liveness_response.result, code);
|
|
97
49
|
|
|
@@ -115,7 +67,7 @@ test('NetworkMessageDirector builds broadcast transaction request and verifies s
|
|
|
115
67
|
const wallet = createWallet();
|
|
116
68
|
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
117
69
|
|
|
118
|
-
const id =
|
|
70
|
+
const id = uuidv7();
|
|
119
71
|
const data = b4a.from('deadbeef', 'hex');
|
|
120
72
|
const caps = ['cap:b', 'cap:a'];
|
|
121
73
|
|
|
@@ -144,19 +96,36 @@ test('NetworkMessageDirector iterates broadcast transaction response ResultCode
|
|
|
144
96
|
const wallet = createWallet();
|
|
145
97
|
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
146
98
|
|
|
147
|
-
const id =
|
|
99
|
+
const id = uuidv7();
|
|
148
100
|
const caps = ['cap:b', 'cap:a'];
|
|
101
|
+
const proof = b4a.from('deadbeef', 'hex');
|
|
102
|
+
const timestamp = Date.now();
|
|
103
|
+
const emptyProof = b4a.alloc(0);
|
|
149
104
|
|
|
150
105
|
for (const code of uniqueResultCodes()) {
|
|
151
|
-
const
|
|
106
|
+
const includeProof = code === NetworkResultCode.OK;
|
|
107
|
+
const proofUnavailable = code === NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE;
|
|
108
|
+
const responseProof = includeProof ? proof : emptyProof;
|
|
109
|
+
const responseTimestamp = includeProof || proofUnavailable ? timestamp : 0;
|
|
110
|
+
const payload = await director.buildBroadcastTransactionResponse(
|
|
111
|
+
id,
|
|
112
|
+
caps,
|
|
113
|
+
code,
|
|
114
|
+
responseProof,
|
|
115
|
+
responseTimestamp
|
|
116
|
+
);
|
|
152
117
|
t.is(payload.type, NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE);
|
|
153
118
|
t.is(payload.broadcast_transaction_response.result, code);
|
|
119
|
+
t.alike(payload.broadcast_transaction_response.proof, responseProof);
|
|
120
|
+
t.is(payload.broadcast_transaction_response.timestamp, responseTimestamp);
|
|
154
121
|
|
|
155
122
|
const msg = createMessage(
|
|
156
123
|
payload.type,
|
|
157
124
|
idToBuffer(payload.id),
|
|
158
125
|
timestampToBuffer(payload.timestamp),
|
|
159
126
|
payload.broadcast_transaction_response.nonce,
|
|
127
|
+
responseProof,
|
|
128
|
+
timestampToBuffer(responseTimestamp),
|
|
160
129
|
safeWriteUInt32BE(code, 0),
|
|
161
130
|
encodeCapabilities(caps)
|
|
162
131
|
);
|
|
@@ -165,37 +134,214 @@ test('NetworkMessageDirector iterates broadcast transaction response ResultCode
|
|
|
165
134
|
|
|
166
135
|
const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
167
136
|
t.is(decoded.broadcast_transaction_response.result, code);
|
|
137
|
+
t.alike(decoded.broadcast_transaction_response.proof, responseProof);
|
|
138
|
+
t.is(decoded.broadcast_transaction_response.timestamp, responseTimestamp);
|
|
168
139
|
}
|
|
169
140
|
});
|
|
170
141
|
|
|
171
|
-
test('NetworkMessageDirector
|
|
142
|
+
test('NetworkMessageDirector builds broadcast transaction response with proof and timestamp', async t => {
|
|
172
143
|
const wallet = createWallet();
|
|
173
144
|
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
174
145
|
|
|
175
|
-
const id =
|
|
146
|
+
const id = uuidv7();
|
|
176
147
|
const caps = ['cap:b', 'cap:a'];
|
|
177
|
-
const
|
|
148
|
+
const proof = b4a.from('deadbeef', 'hex');
|
|
149
|
+
const timestamp = Date.now();
|
|
178
150
|
|
|
179
|
-
|
|
180
|
-
|
|
151
|
+
const payload = await director.buildBroadcastTransactionResponse(
|
|
152
|
+
id,
|
|
153
|
+
caps,
|
|
154
|
+
NetworkResultCode.OK,
|
|
155
|
+
proof,
|
|
156
|
+
timestamp
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
t.alike(payload.broadcast_transaction_response.proof, proof);
|
|
160
|
+
t.is(payload.broadcast_transaction_response.timestamp, timestamp);
|
|
161
|
+
|
|
162
|
+
const msg = createMessage(
|
|
163
|
+
payload.type,
|
|
164
|
+
idToBuffer(payload.id),
|
|
165
|
+
timestampToBuffer(payload.timestamp),
|
|
166
|
+
payload.broadcast_transaction_response.nonce,
|
|
167
|
+
proof,
|
|
168
|
+
timestampToBuffer(timestamp),
|
|
169
|
+
safeWriteUInt32BE(NetworkResultCode.OK, 0),
|
|
170
|
+
encodeCapabilities(caps)
|
|
171
|
+
);
|
|
172
|
+
const hash = await PeerWallet.blake3(msg);
|
|
173
|
+
t.ok(wallet.verify(payload.broadcast_transaction_response.signature, hash, wallet.publicKey));
|
|
174
|
+
|
|
175
|
+
const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
176
|
+
t.alike(decoded.broadcast_transaction_response.proof, proof);
|
|
177
|
+
t.is(decoded.broadcast_transaction_response.timestamp, timestamp);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('NetworkMessageDirector rejects OK response when proof is provided without timestamp', async t => {
|
|
181
|
+
const wallet = createWallet();
|
|
182
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
183
|
+
|
|
184
|
+
const id = uuidv7();
|
|
185
|
+
const caps = ['cap:b', 'cap:a'];
|
|
186
|
+
const proof = b4a.from('deadbeef', 'hex');
|
|
187
|
+
|
|
188
|
+
await t.exception(
|
|
189
|
+
() => director.buildBroadcastTransactionResponse(
|
|
181
190
|
id,
|
|
182
|
-
otherAddress,
|
|
183
191
|
caps,
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
NetworkResultCode.OK,
|
|
193
|
+
proof
|
|
194
|
+
),
|
|
195
|
+
errorMessageIncludes('Result code OK requires non-empty proof and timestamp > 0.')
|
|
196
|
+
);
|
|
197
|
+
});
|
|
188
198
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
test('NetworkMessageDirector rejects OK response when timestamp is provided without proof', async t => {
|
|
200
|
+
const wallet = createWallet();
|
|
201
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
202
|
+
|
|
203
|
+
const id = uuidv7();
|
|
204
|
+
const caps = ['cap:b', 'cap:a'];
|
|
205
|
+
const timestamp = Date.now();
|
|
206
|
+
|
|
207
|
+
await t.exception(
|
|
208
|
+
() => director.buildBroadcastTransactionResponse(
|
|
209
|
+
id,
|
|
210
|
+
caps,
|
|
211
|
+
NetworkResultCode.OK,
|
|
212
|
+
null,
|
|
213
|
+
timestamp
|
|
214
|
+
),
|
|
215
|
+
errorMessageIncludes('Result code OK requires non-empty proof and timestamp > 0.')
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('NetworkMessageDirector allows TX_ACCEPTED_PROOF_UNAVAILABLE response with timestamp and empty proof', async t => {
|
|
220
|
+
const wallet = createWallet();
|
|
221
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
222
|
+
|
|
223
|
+
const id = uuidv7();
|
|
224
|
+
const caps = ['cap:b', 'cap:a'];
|
|
225
|
+
const timestamp = Date.now();
|
|
226
|
+
const emptyProof = b4a.alloc(0);
|
|
227
|
+
|
|
228
|
+
const payload = await director.buildBroadcastTransactionResponse(
|
|
229
|
+
id,
|
|
230
|
+
caps,
|
|
231
|
+
NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE,
|
|
232
|
+
emptyProof,
|
|
233
|
+
timestamp
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
t.alike(payload.broadcast_transaction_response.proof, emptyProof);
|
|
237
|
+
t.is(payload.broadcast_transaction_response.timestamp, timestamp);
|
|
238
|
+
t.is(payload.broadcast_transaction_response.result, NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE);
|
|
239
|
+
|
|
240
|
+
const msg = createMessage(
|
|
241
|
+
payload.type,
|
|
242
|
+
idToBuffer(payload.id),
|
|
243
|
+
timestampToBuffer(payload.timestamp),
|
|
244
|
+
payload.broadcast_transaction_response.nonce,
|
|
245
|
+
emptyProof,
|
|
246
|
+
timestampToBuffer(timestamp),
|
|
247
|
+
safeWriteUInt32BE(NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE, 0),
|
|
248
|
+
encodeCapabilities(caps)
|
|
249
|
+
);
|
|
250
|
+
const hash = await PeerWallet.blake3(msg);
|
|
251
|
+
t.ok(wallet.verify(payload.broadcast_transaction_response.signature, hash, wallet.publicKey));
|
|
252
|
+
|
|
253
|
+
const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
254
|
+
t.is(decoded.broadcast_transaction_response.timestamp, timestamp);
|
|
255
|
+
t.is(decoded.broadcast_transaction_response.result, NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('NetworkMessageDirector rejects OK response when proof and timestamp are both missing', async t => {
|
|
259
|
+
const wallet = createWallet();
|
|
260
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
261
|
+
|
|
262
|
+
const id = uuidv7();
|
|
263
|
+
const caps = ['cap:b', 'cap:a'];
|
|
264
|
+
|
|
265
|
+
await t.exception(
|
|
266
|
+
() => director.buildBroadcastTransactionResponse(
|
|
267
|
+
id,
|
|
268
|
+
caps,
|
|
269
|
+
NetworkResultCode.OK
|
|
270
|
+
),
|
|
271
|
+
errorMessageIncludes('Result code OK requires non-empty proof and timestamp > 0.')
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('NetworkMessageDirector rejects TX_ACCEPTED_PROOF_UNAVAILABLE response when timestamp is missing', async t => {
|
|
276
|
+
const wallet = createWallet();
|
|
277
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
278
|
+
|
|
279
|
+
const id = uuidv7();
|
|
280
|
+
const caps = ['cap:b', 'cap:a'];
|
|
281
|
+
|
|
282
|
+
await t.exception(
|
|
283
|
+
() => director.buildBroadcastTransactionResponse(
|
|
284
|
+
id,
|
|
285
|
+
caps,
|
|
286
|
+
NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE
|
|
287
|
+
),
|
|
288
|
+
errorMessageIncludes('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires timestamp > 0.')
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('NetworkMessageDirector rejects TX_ACCEPTED_PROOF_UNAVAILABLE response when proof is non-empty', async t => {
|
|
293
|
+
const wallet = createWallet();
|
|
294
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
295
|
+
|
|
296
|
+
const id = uuidv7();
|
|
297
|
+
const caps = ['cap:b', 'cap:a'];
|
|
298
|
+
|
|
299
|
+
await t.exception(
|
|
300
|
+
() => director.buildBroadcastTransactionResponse(
|
|
301
|
+
id,
|
|
302
|
+
caps,
|
|
303
|
+
NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE,
|
|
304
|
+
b4a.from('deadbeef', 'hex'),
|
|
305
|
+
Date.now()
|
|
306
|
+
),
|
|
307
|
+
errorMessageIncludes('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires empty proof.')
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('NetworkMessageDirector rejects non-OK response when proof is non-empty', async t => {
|
|
312
|
+
const wallet = createWallet();
|
|
313
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
314
|
+
|
|
315
|
+
const id = uuidv7();
|
|
316
|
+
const caps = ['cap:b', 'cap:a'];
|
|
317
|
+
|
|
318
|
+
await t.exception(
|
|
319
|
+
() => director.buildBroadcastTransactionResponse(
|
|
320
|
+
id,
|
|
321
|
+
caps,
|
|
322
|
+
NetworkResultCode.INVALID_PAYLOAD,
|
|
323
|
+
b4a.from('deadbeef', 'hex'),
|
|
324
|
+
0
|
|
325
|
+
),
|
|
326
|
+
errorMessageIncludes('Non-OK result code requires empty proof.')
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test('NetworkMessageDirector rejects non-OK response with timestamp > 0 unless proof is unavailable', async t => {
|
|
331
|
+
const wallet = createWallet();
|
|
332
|
+
const director = new NetworkMessageDirector(new NetworkMessageBuilder(wallet, config));
|
|
333
|
+
|
|
334
|
+
const id = uuidv7();
|
|
335
|
+
const caps = ['cap:b', 'cap:a'];
|
|
336
|
+
|
|
337
|
+
await t.exception(
|
|
338
|
+
() => director.buildBroadcastTransactionResponse(
|
|
339
|
+
id,
|
|
340
|
+
caps,
|
|
341
|
+
NetworkResultCode.INVALID_PAYLOAD,
|
|
342
|
+
null,
|
|
343
|
+
Date.now()
|
|
344
|
+
),
|
|
345
|
+
errorMessageIncludes('Non-OK result code requires timestamp to be 0, except TX_ACCEPTED_PROOF_UNAVAILABLE.')
|
|
346
|
+
);
|
|
201
347
|
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { test } from 'brittle';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import b4a from 'b4a';
|
|
4
|
+
import { config } from '../../helpers/config.js';
|
|
5
|
+
import NetworkMessageRouter from '../../../src/core/network/protocols/legacy/NetworkMessageRouter.js';
|
|
6
|
+
import LegacyGetRequestHandler from '../../../src/core/network/protocols/legacy/handlers/LegacyGetRequestHandler.js';
|
|
7
|
+
import LegacyResponseHandler from '../../../src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js';
|
|
8
|
+
import { NETWORK_MESSAGE_TYPES } from '../../../src/utils/constants.js';
|
|
9
|
+
|
|
10
|
+
const makeConnection = (sandbox) => ({
|
|
11
|
+
remotePublicKey: b4a.alloc(32, 0x01),
|
|
12
|
+
protocolSession: {
|
|
13
|
+
setLegacyAsPreferredProtocol: sandbox.stub()
|
|
14
|
+
},
|
|
15
|
+
end: sandbox.stub()
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const makeRouterContext = (t) => {
|
|
19
|
+
const sandbox = sinon.createSandbox();
|
|
20
|
+
t.teardown(() => sandbox.restore());
|
|
21
|
+
|
|
22
|
+
const getHandler = sandbox.stub(LegacyGetRequestHandler.prototype, 'handle').resolves();
|
|
23
|
+
const responseHandler = sandbox.stub(LegacyResponseHandler.prototype, 'handle').resolves();
|
|
24
|
+
const router = new NetworkMessageRouter({}, { address: 'test-wallet' }, {}, {}, config);
|
|
25
|
+
const connection = makeConnection(sandbox);
|
|
26
|
+
|
|
27
|
+
return { connection, getHandler, responseHandler, router };
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
test('LegacyNetworkMessageRouter', async (t) => {
|
|
31
|
+
await t.test('routes legacy string GET messages', async (t) => {
|
|
32
|
+
const { connection, getHandler, responseHandler, router } = makeRouterContext(t);
|
|
33
|
+
const message = NETWORK_MESSAGE_TYPES.GET.VALIDATOR;
|
|
34
|
+
|
|
35
|
+
await router.route(message, connection);
|
|
36
|
+
|
|
37
|
+
t.ok(connection.protocolSession.setLegacyAsPreferredProtocol.calledOnce, 'should prefer legacy protocol');
|
|
38
|
+
t.ok(getHandler.calledOnce, 'should route GET message');
|
|
39
|
+
t.ok(responseHandler.notCalled, 'should not route response handler');
|
|
40
|
+
t.is(getHandler.firstCall.args[0], message, 'passes GET message through to handler');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await t.test('routes legacy object response messages', async (t) => {
|
|
44
|
+
const { connection, getHandler, responseHandler, router } = makeRouterContext(t);
|
|
45
|
+
const message = { op: NETWORK_MESSAGE_TYPES.RESPONSE.VALIDATOR };
|
|
46
|
+
|
|
47
|
+
await router.route(message, connection);
|
|
48
|
+
|
|
49
|
+
t.ok(connection.protocolSession.setLegacyAsPreferredProtocol.calledOnce, 'should prefer legacy protocol');
|
|
50
|
+
t.ok(responseHandler.calledOnce, 'should route response message');
|
|
51
|
+
t.ok(getHandler.notCalled, 'should not route GET handler');
|
|
52
|
+
t.is(responseHandler.firstCall.args[0], message, 'passes response message through to handler');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { test } from 'brittle';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import b4a from 'b4a';
|
|
4
|
+
|
|
5
|
+
import NetworkWalletFactory from '../../../src/core/network/identity/NetworkWalletFactory.js';
|
|
6
|
+
import ProtocolSession from '../../../src/core/network/protocols/ProtocolSession.js';
|
|
7
|
+
import { ResultCode } from '../../../src/utils/constants.js';
|
|
8
|
+
import { config } from '../../helpers/config.js';
|
|
9
|
+
import { testKeyPair1 } from '../../fixtures/apply.fixtures.js';
|
|
10
|
+
|
|
11
|
+
function createWallet() {
|
|
12
|
+
const keyPair = {
|
|
13
|
+
publicKey: b4a.from(testKeyPair1.publicKey, 'hex'),
|
|
14
|
+
secretKey: b4a.from(testKeyPair1.secretKey, 'hex')
|
|
15
|
+
};
|
|
16
|
+
return NetworkWalletFactory.provide({
|
|
17
|
+
enableWallet: false,
|
|
18
|
+
keyPair,
|
|
19
|
+
networkPrefix: config.addressPrefix
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function makeProtocol(sendStub) {
|
|
24
|
+
return {
|
|
25
|
+
send: sendStub ?? sinon.stub().resolves(ResultCode.OK),
|
|
26
|
+
sendAndForget: sinon.stub(),
|
|
27
|
+
decode: sinon.stub(),
|
|
28
|
+
close: sinon.stub()
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('ProtocolSession', (t) => {
|
|
33
|
+
t.teardown(() => sinon.restore());
|
|
34
|
+
|
|
35
|
+
test('probe sets preferred protocol to v1 on OK', async (t) => {
|
|
36
|
+
const v1Send = sinon.stub().resolves(ResultCode.OK);
|
|
37
|
+
const session = new ProtocolSession(
|
|
38
|
+
makeProtocol(),
|
|
39
|
+
makeProtocol(v1Send),
|
|
40
|
+
createWallet(),
|
|
41
|
+
config
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
await session.probe();
|
|
45
|
+
t.is(session.preferredProtocol, session.supportedProtocols.V1);
|
|
46
|
+
t.ok(v1Send.calledOnce);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('probe sets preferred protocol to legacy on non-OK', async (t) => {
|
|
50
|
+
const v1Send = sinon.stub().resolves(ResultCode.TIMEOUT);
|
|
51
|
+
const session = new ProtocolSession(
|
|
52
|
+
makeProtocol(),
|
|
53
|
+
makeProtocol(v1Send),
|
|
54
|
+
createWallet(),
|
|
55
|
+
config
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
await session.probe();
|
|
59
|
+
t.is(session.preferredProtocol, session.supportedProtocols.LEGACY);
|
|
60
|
+
t.ok(v1Send.calledOnce);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('probe sets preferred protocol to legacy on rejection', async (t) => {
|
|
64
|
+
const v1Send = sinon.stub().rejects(new Error('boom'));
|
|
65
|
+
const session = new ProtocolSession(
|
|
66
|
+
makeProtocol(),
|
|
67
|
+
makeProtocol(v1Send),
|
|
68
|
+
createWallet(),
|
|
69
|
+
config
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
await session.probe();
|
|
73
|
+
t.is(session.preferredProtocol, session.supportedProtocols.LEGACY);
|
|
74
|
+
t.ok(v1Send.calledOnce);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('sendHealthCheck returns OK when preferred is v1', async (t) => {
|
|
78
|
+
const v1Send = sinon.stub().resolves(ResultCode.OK);
|
|
79
|
+
const session = new ProtocolSession(
|
|
80
|
+
makeProtocol(),
|
|
81
|
+
makeProtocol(v1Send),
|
|
82
|
+
createWallet(),
|
|
83
|
+
config
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
session.setV1AsPreferredProtocol();
|
|
87
|
+
const result = await session.sendHealthCheck();
|
|
88
|
+
t.is(result, ResultCode.OK);
|
|
89
|
+
t.ok(v1Send.calledOnce);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('sendHealthCheck returns OK when preferred is legacy', async (t) => {
|
|
93
|
+
const session = new ProtocolSession(
|
|
94
|
+
makeProtocol(),
|
|
95
|
+
makeProtocol(),
|
|
96
|
+
createWallet(),
|
|
97
|
+
config
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
session.setLegacyAsPreferredProtocol();
|
|
101
|
+
const result = await session.sendHealthCheck();
|
|
102
|
+
t.is(result, ResultCode.OK);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('sendHealthCheck returns UNSPECIFIED when not probed', async (t) => {
|
|
106
|
+
const session = new ProtocolSession(
|
|
107
|
+
makeProtocol(),
|
|
108
|
+
makeProtocol(),
|
|
109
|
+
createWallet(),
|
|
110
|
+
config
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const result = await session.sendHealthCheck();
|
|
114
|
+
t.is(result, ResultCode.UNSPECIFIED);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('isHealthCheckSupported throws when not probed', async (t) => {
|
|
118
|
+
const session = new ProtocolSession(
|
|
119
|
+
makeProtocol(),
|
|
120
|
+
makeProtocol(),
|
|
121
|
+
createWallet(),
|
|
122
|
+
config
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
await t.exception.all(() => session.isHealthCheckSupported());
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -2,8 +2,11 @@ import { default as test } from 'brittle';
|
|
|
2
2
|
|
|
3
3
|
async function runNetworkModuleTests() {
|
|
4
4
|
test.pause();
|
|
5
|
-
await import('./
|
|
5
|
+
await import('./LegacyNetworkMessageRouter.test.js');
|
|
6
6
|
await import('./NetworkWalletFactory.test.js');
|
|
7
|
+
await import('./ProtocolSession.test.js');
|
|
8
|
+
await import('./services/services.test.js');
|
|
9
|
+
await import('./v1/v1.test.js');
|
|
7
10
|
test.resume();
|
|
8
11
|
}
|
|
9
12
|
|