trac-msb 0.2.9 → 0.2.11
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/CODE_OF_CONDUCT.md +128 -0
- package/README.md +33 -18
- package/docker-compose.yml +1 -0
- package/docs/trac_network_http_api.openapi.yaml +889 -0
- package/msb.mjs +4 -21
- package/package.json +16 -12
- 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/handlers.js +163 -90
- package/rpc/routes/v1.js +3 -1
- package/rpc/rpc_server.js +3 -3
- package/rpc/rpc_services.js +45 -31
- package/rpc/utils/helpers.js +82 -51
- package/scripts/generate-protobufs.js +37 -12
- package/src/config/args.js +46 -0
- package/src/config/config.js +99 -5
- package/src/config/env.js +86 -7
- package/src/core/network/Network.js +79 -46
- 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 +26 -20
- package/src/core/network/protocols/{shared/handlers/base/BaseOperationHandler.js → legacy/handlers/BaseStateOperationHandler.js} +25 -15
- 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} +20 -13
- package/src/core/network/protocols/{shared/handlers/SubnetworkOperationHandler.js → legacy/handlers/LegacySubnetworkOperationHandler.js} +29 -18
- package/src/core/network/protocols/{shared/handlers/TransferOperationHandler.js → legacy/handlers/LegacyTransferOperationHandler.js} +18 -12
- package/src/core/network/protocols/legacy/validators/base/BaseResponse.js +1 -1
- 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 +147 -95
- package/src/core/network/services/MessageOrchestrator.js +152 -28
- 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 +133 -22
- package/src/core/network/services/TransactionRateLimiterService.js +57 -42
- package/src/core/network/services/ValidatorHealthCheckService.js +127 -0
- package/src/core/network/services/ValidatorObserverService.js +23 -32
- package/src/core/state/State.js +72 -22
- package/src/index.js +8 -5
- package/src/messages/network/v1/NetworkMessageBuilder.js +61 -81
- package/src/messages/network/v1/NetworkMessageDirector.js +16 -50
- package/src/messages/state/ApplyStateMessageBuilder.js +1 -1
- package/src/utils/Scheduler.js +0 -8
- package/src/utils/check.js +1 -1
- package/src/utils/constants.js +68 -19
- package/src/utils/deepEqualApplyPayload.js +40 -0
- package/src/utils/fileUtils.js +13 -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/src/utils/type.js +26 -0
- package/tests/acceptance/v1/account/account.test.mjs +8 -2
- package/tests/acceptance/v1/balance/balance.test.mjs +1 -2
- package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +26 -30
- package/tests/acceptance/v1/health/health.test.mjs +33 -0
- package/tests/acceptance/v1/rpc.test.mjs +3 -2
- package/tests/acceptance/v1/tx/tx.test.mjs +50 -17
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +60 -18
- package/tests/fixtures/check.fixtures.js +33 -32
- package/tests/fixtures/networkV1.fixtures.js +2 -27
- package/tests/fixtures/protobuf.fixtures.js +33 -32
- package/tests/helpers/StateNetworkFactory.js +2 -2
- package/tests/helpers/address.js +6 -0
- package/tests/helpers/autobaseTestHelpers.js +2 -1
- package/tests/helpers/config.js +2 -1
- package/tests/helpers/setupApplyTests.js +6 -10
- package/tests/helpers/transactionPayloads.mjs +2 -2
- package/tests/unit/messages/network/NetworkMessageBuilder.test.js +241 -81
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +225 -81
- 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/fileUtils/readAddressesFromWhitelistFile.test.js +4 -3
- package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +3 -2
- package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +3 -2
- package/tests/unit/utils/protobuf/operationHelpers.test.js +2 -4
- package/tests/unit/utils/type/type.test.js +25 -0
- package/tests/unit/utils/utils.test.js +2 -0
- package/.github/workflows/acceptance-tests.yml +0 -42
- package/.github/workflows/publish.yml +0 -33
- package/.github/workflows/unit-tests.yml +0 -40
- 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
|
@@ -82,11 +82,10 @@ export async function fundPeer(admin, toFund, amount) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export async function initMsbPeer(peerName, peerKeyPair, temporaryDirectory, options = {}) {
|
|
85
|
-
const
|
|
86
|
-
peer
|
|
87
|
-
peer.options
|
|
88
|
-
peer.
|
|
89
|
-
peer.config = createConfig(ENV.DEVELOPMENT, peer.options)
|
|
85
|
+
const config = createConfig(ENV.DEVELOPMENT, { ...options, storesDirectory: `${temporaryDirectory}/${peerName}` })
|
|
86
|
+
const peer = await initDirectoryStructure(peerName, peerKeyPair, config.storesFullPath);
|
|
87
|
+
peer.options = { ...options, storesDirectory: config.storesDirectory }
|
|
88
|
+
peer.config = config
|
|
90
89
|
const msb = new MainSettlementBus(peer.config);
|
|
91
90
|
|
|
92
91
|
peer.msb = msb;
|
|
@@ -290,9 +289,7 @@ export async function removeTemporaryDirectory(temporaryDirectory) {
|
|
|
290
289
|
export async function initDirectoryStructure(peerName, keyPair, temporaryDirectory) {
|
|
291
290
|
try {
|
|
292
291
|
await ensureEnvReady();
|
|
293
|
-
const
|
|
294
|
-
const storeName = peerName + '/';
|
|
295
|
-
const corestoreDbDirectory = path.join(storesDirectory, storeName, 'db');
|
|
292
|
+
const corestoreDbDirectory = path.join(temporaryDirectory, 'db');
|
|
296
293
|
await fsp.mkdir(corestoreDbDirectory, {recursive: true});
|
|
297
294
|
|
|
298
295
|
const keypath = path.join(corestoreDbDirectory, 'keypair.json');
|
|
@@ -303,8 +300,7 @@ export async function initDirectoryStructure(peerName, keyPair, temporaryDirecto
|
|
|
303
300
|
await wallet.ready
|
|
304
301
|
await wallet.exportToFile(keypath)
|
|
305
302
|
return {
|
|
306
|
-
storesDirectory,
|
|
307
|
-
storeName,
|
|
303
|
+
storesDirectory: temporaryDirectory,
|
|
308
304
|
corestoreDbDirectory,
|
|
309
305
|
keypath,
|
|
310
306
|
}
|
|
@@ -23,10 +23,10 @@ export const waitForConnection = async node => {
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Build a base64-encoded transfer payload and matching tx hash
|
|
26
|
-
* that are compatible with MSB's
|
|
26
|
+
* that are compatible with MSB's PartialTransferValidator validator.
|
|
27
27
|
*
|
|
28
28
|
* This helper mirrors the hashing/signing logic used by
|
|
29
|
-
*
|
|
29
|
+
* PartialOperationValidator.validateSignature, so that tests broadcast
|
|
30
30
|
* transactions the node will accept without touching consensus code.
|
|
31
31
|
*
|
|
32
32
|
* @param {object} context - General context
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { test } from 'brittle';
|
|
2
2
|
import b4a from 'b4a';
|
|
3
3
|
import PeerWallet from 'trac-wallet';
|
|
4
|
-
import { TRAC_NETWORK_MSB_MAINNET_PREFIX } from 'trac-wallet/constants.js';
|
|
5
4
|
import { v7 as uuidv7 } from 'uuid';
|
|
6
5
|
import NetworkWalletFactory from '../../../../src/core/network/identity/NetworkWalletFactory.js';
|
|
7
6
|
import NetworkMessageBuilder from '../../../../src/messages/network/v1/NetworkMessageBuilder.js';
|
|
@@ -18,8 +17,8 @@ import {
|
|
|
18
17
|
idToBuffer,
|
|
19
18
|
timestampToBuffer
|
|
20
19
|
} from '../../../../src/utils/buffer.js';
|
|
21
|
-
import { addressToBuffer } from '../../../../src/core/state/utils/address.js';
|
|
22
20
|
import { config } from '../../../helpers/config.js';
|
|
21
|
+
import { asAddress } from '../../../helpers/address.js';
|
|
23
22
|
import { testKeyPair1 } from '../../../fixtures/apply.fixtures.js';
|
|
24
23
|
|
|
25
24
|
function createWallet() {
|
|
@@ -30,7 +29,7 @@ function createWallet() {
|
|
|
30
29
|
return NetworkWalletFactory.provide({
|
|
31
30
|
enableWallet: false,
|
|
32
31
|
keyPair,
|
|
33
|
-
networkPrefix:
|
|
32
|
+
networkPrefix: config.addressPrefix
|
|
34
33
|
});
|
|
35
34
|
}
|
|
36
35
|
|
|
@@ -38,81 +37,6 @@ function uniqueResultCodes() {
|
|
|
38
37
|
return [...new Set(Object.values(NetworkResultCode))].sort((a, b) => a - b);
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
test('NetworkMessageBuilder builds validator connection request and verifies signature', async t => {
|
|
42
|
-
const wallet = createWallet();
|
|
43
|
-
const builder = new NetworkMessageBuilder(wallet, config);
|
|
44
|
-
|
|
45
|
-
const id = uuidv7();
|
|
46
|
-
const caps = ['cap:b', 'cap:a'];
|
|
47
|
-
|
|
48
|
-
await builder
|
|
49
|
-
.setType(NetworkOperationType.VALIDATOR_CONNECTION_REQUEST)
|
|
50
|
-
.setId(id)
|
|
51
|
-
.setTimestamp()
|
|
52
|
-
.setIssuerAddress(wallet.address)
|
|
53
|
-
.setCapabilities(caps)
|
|
54
|
-
.buildPayload();
|
|
55
|
-
|
|
56
|
-
const payload = builder.getResult();
|
|
57
|
-
t.is(payload.type, NetworkOperationType.VALIDATOR_CONNECTION_REQUEST);
|
|
58
|
-
t.is(payload.id, id);
|
|
59
|
-
t.alike(payload.capabilities, caps);
|
|
60
|
-
t.ok(b4a.isBuffer(payload.validator_connection_request.nonce));
|
|
61
|
-
t.ok(b4a.isBuffer(payload.validator_connection_request.signature));
|
|
62
|
-
|
|
63
|
-
const message = createMessage(
|
|
64
|
-
payload.type,
|
|
65
|
-
idToBuffer(id),
|
|
66
|
-
timestampToBuffer(payload.timestamp),
|
|
67
|
-
addressToBuffer(wallet.address, config.addressPrefix),
|
|
68
|
-
payload.validator_connection_request.nonce,
|
|
69
|
-
encodeCapabilities(caps)
|
|
70
|
-
);
|
|
71
|
-
const hash = await PeerWallet.blake3(message);
|
|
72
|
-
t.ok(wallet.verify(payload.validator_connection_request.signature, hash, wallet.publicKey));
|
|
73
|
-
|
|
74
|
-
const roundTrip = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
75
|
-
t.is(roundTrip.type, NetworkOperationType.VALIDATOR_CONNECTION_REQUEST);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test('NetworkMessageBuilder iterates validator connection response ResultCode values', async t => {
|
|
79
|
-
const wallet = createWallet();
|
|
80
|
-
const builder = new NetworkMessageBuilder(wallet, config);
|
|
81
|
-
const id = uuidv7();
|
|
82
|
-
const otherAddress = 'trac1xm76l9qaujh7vqktk8302mw9sfrxau3l45w62hqfl4kasswt6yts0autkh';
|
|
83
|
-
const caps = ['cap:b', 'cap:a'];
|
|
84
|
-
|
|
85
|
-
for (const code of uniqueResultCodes()) {
|
|
86
|
-
await builder
|
|
87
|
-
.setType(NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE)
|
|
88
|
-
.setId(id)
|
|
89
|
-
.setTimestamp()
|
|
90
|
-
.setIssuerAddress(otherAddress)
|
|
91
|
-
.setCapabilities(caps)
|
|
92
|
-
.setResultCode(code)
|
|
93
|
-
.buildPayload();
|
|
94
|
-
|
|
95
|
-
const payload = builder.getResult();
|
|
96
|
-
t.is(payload.type, NetworkOperationType.VALIDATOR_CONNECTION_RESPONSE);
|
|
97
|
-
t.is(payload.validator_connection_response.result, code);
|
|
98
|
-
|
|
99
|
-
const msg = createMessage(
|
|
100
|
-
payload.type,
|
|
101
|
-
idToBuffer(payload.id),
|
|
102
|
-
timestampToBuffer(payload.timestamp),
|
|
103
|
-
addressToBuffer(otherAddress, config.addressPrefix),
|
|
104
|
-
payload.validator_connection_response.nonce,
|
|
105
|
-
safeWriteUInt32BE(code, 0),
|
|
106
|
-
encodeCapabilities(caps)
|
|
107
|
-
);
|
|
108
|
-
const hash = await PeerWallet.blake3(msg);
|
|
109
|
-
t.ok(wallet.verify(payload.validator_connection_response.signature, hash, wallet.publicKey));
|
|
110
|
-
|
|
111
|
-
const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
112
|
-
t.is(decoded.validator_connection_response.result, code);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
40
|
test('NetworkMessageBuilder iterates liveness response ResultCode values', async t => {
|
|
117
41
|
const wallet = createWallet();
|
|
118
42
|
const builder = new NetworkMessageBuilder(wallet, config);
|
|
@@ -125,7 +49,6 @@ test('NetworkMessageBuilder iterates liveness response ResultCode values', async
|
|
|
125
49
|
.setType(NetworkOperationType.LIVENESS_RESPONSE)
|
|
126
50
|
.setId(id)
|
|
127
51
|
.setTimestamp()
|
|
128
|
-
.setData(data)
|
|
129
52
|
.setCapabilities(caps)
|
|
130
53
|
.setResultCode(code)
|
|
131
54
|
.buildPayload();
|
|
@@ -156,13 +79,11 @@ test('NetworkMessageBuilder builds liveness request and verifies signature (data
|
|
|
156
79
|
|
|
157
80
|
const id = uuidv7();
|
|
158
81
|
const caps = ['cap:b', 'cap:a'];
|
|
159
|
-
const data = b4a.from('ping', 'utf8');
|
|
160
82
|
|
|
161
83
|
await builder
|
|
162
84
|
.setType(NetworkOperationType.LIVENESS_REQUEST)
|
|
163
85
|
.setId(id)
|
|
164
86
|
.setTimestamp()
|
|
165
|
-
.setData(data)
|
|
166
87
|
.setCapabilities(caps)
|
|
167
88
|
.buildPayload();
|
|
168
89
|
|
|
@@ -187,36 +108,275 @@ test('NetworkMessageBuilder iterates broadcast transaction response ResultCode v
|
|
|
187
108
|
const builder = new NetworkMessageBuilder(wallet, config);
|
|
188
109
|
const id = uuidv7();
|
|
189
110
|
const caps = ['cap:b', 'cap:a'];
|
|
111
|
+
const proof = b4a.from('deadbeef', 'hex');
|
|
112
|
+
const timestamp = Date.now();
|
|
113
|
+
const emptyProof = b4a.alloc(0);
|
|
190
114
|
|
|
191
115
|
for (const code of uniqueResultCodes()) {
|
|
116
|
+
const includeProof = code === NetworkResultCode.OK;
|
|
117
|
+
const proofUnavailable = code === NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE;
|
|
118
|
+
const responseProof = includeProof ? proof : emptyProof;
|
|
119
|
+
const responseTimestampLedger = includeProof || proofUnavailable ? timestamp : 0;
|
|
192
120
|
await builder
|
|
193
121
|
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
194
122
|
.setId(id)
|
|
195
123
|
.setTimestamp()
|
|
196
124
|
.setCapabilities(caps)
|
|
125
|
+
.setProof(responseProof)
|
|
126
|
+
.setTimestampLedger(responseTimestampLedger)
|
|
197
127
|
.setResultCode(code)
|
|
198
128
|
.buildPayload();
|
|
199
129
|
|
|
200
130
|
const payload = builder.getResult();
|
|
201
131
|
t.is(payload.type, NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE);
|
|
202
132
|
t.is(payload.broadcast_transaction_response.result, code);
|
|
133
|
+
t.alike(payload.broadcast_transaction_response.proof, responseProof);
|
|
134
|
+
t.is(payload.broadcast_transaction_response.timestamp, responseTimestampLedger);
|
|
203
135
|
|
|
204
136
|
const msg = createMessage(
|
|
205
137
|
payload.type,
|
|
206
138
|
idToBuffer(payload.id),
|
|
207
139
|
timestampToBuffer(payload.timestamp),
|
|
208
140
|
payload.broadcast_transaction_response.nonce,
|
|
141
|
+
responseProof,
|
|
142
|
+
timestampToBuffer(responseTimestampLedger),
|
|
209
143
|
safeWriteUInt32BE(code, 0),
|
|
210
144
|
encodeCapabilities(caps)
|
|
211
145
|
);
|
|
146
|
+
|
|
212
147
|
const hash = await PeerWallet.blake3(msg);
|
|
213
148
|
t.ok(wallet.verify(payload.broadcast_transaction_response.signature, hash, wallet.publicKey));
|
|
214
149
|
|
|
215
150
|
const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
216
151
|
t.is(decoded.broadcast_transaction_response.result, code);
|
|
152
|
+
t.alike(decoded.broadcast_transaction_response.proof, responseProof);
|
|
153
|
+
t.is(decoded.broadcast_transaction_response.timestamp, responseTimestampLedger);
|
|
217
154
|
}
|
|
218
155
|
});
|
|
219
156
|
|
|
157
|
+
test('NetworkMessageBuilder builds broadcast transaction response with proof and timestamp', async t => {
|
|
158
|
+
const wallet = createWallet();
|
|
159
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
160
|
+
const id = uuidv7();
|
|
161
|
+
const caps = ['cap:b', 'cap:a'];
|
|
162
|
+
const proof = b4a.from('deadbeef', 'hex');
|
|
163
|
+
const timestamp = Date.now();
|
|
164
|
+
|
|
165
|
+
await builder
|
|
166
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
167
|
+
.setId(id)
|
|
168
|
+
.setTimestamp()
|
|
169
|
+
.setCapabilities(caps)
|
|
170
|
+
.setProof(proof)
|
|
171
|
+
.setTimestampLedger(timestamp)
|
|
172
|
+
.setResultCode(NetworkResultCode.OK)
|
|
173
|
+
.buildPayload();
|
|
174
|
+
|
|
175
|
+
const payload = builder.getResult();
|
|
176
|
+
t.alike(payload.broadcast_transaction_response.proof, proof);
|
|
177
|
+
t.is(payload.broadcast_transaction_response.timestamp, timestamp);
|
|
178
|
+
|
|
179
|
+
const msg = createMessage(
|
|
180
|
+
payload.type,
|
|
181
|
+
idToBuffer(payload.id),
|
|
182
|
+
timestampToBuffer(payload.timestamp),
|
|
183
|
+
payload.broadcast_transaction_response.nonce,
|
|
184
|
+
proof,
|
|
185
|
+
timestampToBuffer(timestamp),
|
|
186
|
+
safeWriteUInt32BE(NetworkResultCode.OK, 0),
|
|
187
|
+
encodeCapabilities(caps)
|
|
188
|
+
);
|
|
189
|
+
const hash = await PeerWallet.blake3(msg);
|
|
190
|
+
t.ok(wallet.verify(payload.broadcast_transaction_response.signature, hash, wallet.publicKey));
|
|
191
|
+
|
|
192
|
+
const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
193
|
+
t.alike(decoded.broadcast_transaction_response.proof, proof);
|
|
194
|
+
t.is(decoded.broadcast_transaction_response.timestamp, timestamp);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('NetworkMessageBuilder rejects OK response when proof is provided without timestamp', async t => {
|
|
198
|
+
const wallet = createWallet();
|
|
199
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
200
|
+
const id = uuidv7();
|
|
201
|
+
const caps = ['cap:b', 'cap:a'];
|
|
202
|
+
const proof = b4a.from('deadbeef', 'hex');
|
|
203
|
+
|
|
204
|
+
await t.exception(
|
|
205
|
+
() =>
|
|
206
|
+
builder
|
|
207
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
208
|
+
.setId(id)
|
|
209
|
+
.setTimestamp()
|
|
210
|
+
.setCapabilities(caps)
|
|
211
|
+
.setProof(proof)
|
|
212
|
+
.setResultCode(NetworkResultCode.OK)
|
|
213
|
+
.buildPayload(),
|
|
214
|
+
errorMessageIncludes('Result code OK requires non-empty proof and timestamp > 0.')
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('NetworkMessageBuilder rejects OK response when timestamp is provided without proof', async t => {
|
|
219
|
+
const wallet = createWallet();
|
|
220
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
221
|
+
const id = uuidv7();
|
|
222
|
+
const caps = ['cap:b', 'cap:a'];
|
|
223
|
+
const timestamp = Date.now();
|
|
224
|
+
|
|
225
|
+
await t.exception(
|
|
226
|
+
() =>
|
|
227
|
+
builder
|
|
228
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
229
|
+
.setId(id)
|
|
230
|
+
.setTimestamp()
|
|
231
|
+
.setCapabilities(caps)
|
|
232
|
+
.setTimestampLedger(timestamp)
|
|
233
|
+
.setResultCode(NetworkResultCode.OK)
|
|
234
|
+
.buildPayload(),
|
|
235
|
+
errorMessageIncludes('Result code OK requires non-empty proof and timestamp > 0.')
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('NetworkMessageBuilder allows TX_ACCEPTED_PROOF_UNAVAILABLE response with timestamp and empty proof', async t => {
|
|
240
|
+
const wallet = createWallet();
|
|
241
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
242
|
+
const id = uuidv7();
|
|
243
|
+
const caps = ['cap:b', 'cap:a'];
|
|
244
|
+
const timestamp = Date.now();
|
|
245
|
+
const emptyProof = b4a.alloc(0);
|
|
246
|
+
|
|
247
|
+
await builder
|
|
248
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
249
|
+
.setId(id)
|
|
250
|
+
.setTimestamp()
|
|
251
|
+
.setCapabilities(caps)
|
|
252
|
+
.setProof(emptyProof)
|
|
253
|
+
.setTimestampLedger(timestamp)
|
|
254
|
+
.setResultCode(NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE)
|
|
255
|
+
.buildPayload();
|
|
256
|
+
|
|
257
|
+
const payload = builder.getResult();
|
|
258
|
+
t.alike(payload.broadcast_transaction_response.proof, emptyProof);
|
|
259
|
+
t.is(payload.broadcast_transaction_response.timestamp, timestamp);
|
|
260
|
+
t.is(payload.broadcast_transaction_response.result, NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE);
|
|
261
|
+
|
|
262
|
+
const msg = createMessage(
|
|
263
|
+
payload.type,
|
|
264
|
+
idToBuffer(payload.id),
|
|
265
|
+
timestampToBuffer(payload.timestamp),
|
|
266
|
+
payload.broadcast_transaction_response.nonce,
|
|
267
|
+
emptyProof,
|
|
268
|
+
timestampToBuffer(timestamp),
|
|
269
|
+
safeWriteUInt32BE(NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE, 0),
|
|
270
|
+
encodeCapabilities(caps)
|
|
271
|
+
);
|
|
272
|
+
const hash = await PeerWallet.blake3(msg);
|
|
273
|
+
t.ok(wallet.verify(payload.broadcast_transaction_response.signature, hash, wallet.publicKey));
|
|
274
|
+
|
|
275
|
+
const decoded = decodeV1networkOperation(encodeV1networkOperation(payload));
|
|
276
|
+
t.is(decoded.broadcast_transaction_response.timestamp, timestamp);
|
|
277
|
+
t.is(decoded.broadcast_transaction_response.result, NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test('NetworkMessageBuilder rejects OK response when proof and timestamp are both missing', async t => {
|
|
281
|
+
const wallet = createWallet();
|
|
282
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
283
|
+
const id = uuidv7();
|
|
284
|
+
const caps = ['cap:b', 'cap:a'];
|
|
285
|
+
|
|
286
|
+
await t.exception(
|
|
287
|
+
() =>
|
|
288
|
+
builder
|
|
289
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
290
|
+
.setId(id)
|
|
291
|
+
.setTimestamp()
|
|
292
|
+
.setCapabilities(caps)
|
|
293
|
+
.setResultCode(NetworkResultCode.OK)
|
|
294
|
+
.buildPayload(),
|
|
295
|
+
errorMessageIncludes('Result code OK requires non-empty proof and timestamp > 0.')
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('NetworkMessageBuilder rejects TX_ACCEPTED_PROOF_UNAVAILABLE response when timestamp is missing', async t => {
|
|
300
|
+
const wallet = createWallet();
|
|
301
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
302
|
+
const id = uuidv7();
|
|
303
|
+
const caps = ['cap:b', 'cap:a'];
|
|
304
|
+
|
|
305
|
+
await t.exception(
|
|
306
|
+
() =>
|
|
307
|
+
builder
|
|
308
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
309
|
+
.setId(id)
|
|
310
|
+
.setTimestamp()
|
|
311
|
+
.setCapabilities(caps)
|
|
312
|
+
.setResultCode(NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE)
|
|
313
|
+
.buildPayload(),
|
|
314
|
+
errorMessageIncludes('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires timestamp > 0.')
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('NetworkMessageBuilder rejects TX_ACCEPTED_PROOF_UNAVAILABLE response when proof is non-empty', async t => {
|
|
319
|
+
const wallet = createWallet();
|
|
320
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
321
|
+
const id = uuidv7();
|
|
322
|
+
const caps = ['cap:b', 'cap:a'];
|
|
323
|
+
|
|
324
|
+
await t.exception(
|
|
325
|
+
() =>
|
|
326
|
+
builder
|
|
327
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
328
|
+
.setId(id)
|
|
329
|
+
.setTimestamp()
|
|
330
|
+
.setCapabilities(caps)
|
|
331
|
+
.setProof(b4a.from('deadbeef', 'hex'))
|
|
332
|
+
.setTimestampLedger(Date.now())
|
|
333
|
+
.setResultCode(NetworkResultCode.TX_ACCEPTED_PROOF_UNAVAILABLE)
|
|
334
|
+
.buildPayload(),
|
|
335
|
+
errorMessageIncludes('Result code TX_ACCEPTED_PROOF_UNAVAILABLE requires empty proof.')
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('NetworkMessageBuilder rejects non-OK response when proof is non-empty', async t => {
|
|
340
|
+
const wallet = createWallet();
|
|
341
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
342
|
+
const id = uuidv7();
|
|
343
|
+
const caps = ['cap:b', 'cap:a'];
|
|
344
|
+
|
|
345
|
+
await t.exception(
|
|
346
|
+
() =>
|
|
347
|
+
builder
|
|
348
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
349
|
+
.setId(id)
|
|
350
|
+
.setTimestamp()
|
|
351
|
+
.setCapabilities(caps)
|
|
352
|
+
.setProof(b4a.from('deadbeef', 'hex'))
|
|
353
|
+
.setTimestampLedger(0)
|
|
354
|
+
.setResultCode(NetworkResultCode.INVALID_PAYLOAD)
|
|
355
|
+
.buildPayload(),
|
|
356
|
+
errorMessageIncludes('Non-OK result code requires empty proof.')
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test('NetworkMessageBuilder rejects non-OK response with timestamp > 0 unless proof is unavailable', async t => {
|
|
361
|
+
const wallet = createWallet();
|
|
362
|
+
const builder = new NetworkMessageBuilder(wallet, config);
|
|
363
|
+
const id = uuidv7();
|
|
364
|
+
const caps = ['cap:b', 'cap:a'];
|
|
365
|
+
|
|
366
|
+
await t.exception(
|
|
367
|
+
() =>
|
|
368
|
+
builder
|
|
369
|
+
.setType(NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE)
|
|
370
|
+
.setId(id)
|
|
371
|
+
.setTimestamp()
|
|
372
|
+
.setCapabilities(caps)
|
|
373
|
+
.setTimestampLedger(Date.now())
|
|
374
|
+
.setResultCode(NetworkResultCode.INVALID_PAYLOAD)
|
|
375
|
+
.buildPayload(),
|
|
376
|
+
errorMessageIncludes('Non-OK result code requires timestamp to be 0, except TX_ACCEPTED_PROOF_UNAVAILABLE.')
|
|
377
|
+
);
|
|
378
|
+
});
|
|
379
|
+
|
|
220
380
|
test('NetworkMessageBuilder builds broadcast transaction request and verifies signature', async t => {
|
|
221
381
|
const wallet = createWallet();
|
|
222
382
|
const builder = new NetworkMessageBuilder(wallet, config);
|