trac-msb 0.2.7 → 0.2.8
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/publish.yml +8 -16
- package/docs/networking-dualstack-plan.md +75 -0
- package/docs/networking-layer-redesign.md +155 -0
- package/msb.mjs +11 -23
- package/package.json +2 -3
- package/rpc/{create_server.mjs → create_server.js} +2 -2
- package/rpc/{handlers.mjs → handlers.js} +5 -5
- package/rpc/routes/{index.mjs → index.js} +1 -1
- package/rpc/routes/{v1.mjs → v1.js} +1 -1
- package/rpc/{rpc_server.mjs → rpc_server.js} +1 -1
- package/rpc/rpc_services.js +4 -4
- package/src/config/config.js +137 -0
- package/src/config/env.js +61 -0
- package/src/core/network/Network.js +119 -73
- package/src/core/network/identity/NetworkWalletFactory.js +3 -4
- package/src/core/network/messaging/NetworkMessages.js +12 -11
- package/src/core/network/messaging/handlers/GetRequestHandler.js +5 -4
- package/src/core/network/messaging/handlers/ResponseHandler.js +4 -5
- package/src/core/network/messaging/handlers/RoleOperationHandler.js +17 -19
- package/src/core/network/messaging/handlers/SubnetworkOperationHandler.js +44 -38
- package/src/core/network/messaging/handlers/TransferOperationHandler.js +29 -25
- package/src/core/network/messaging/handlers/base/BaseOperationHandler.js +20 -21
- package/src/core/network/messaging/routes/NetworkMessageRouter.js +24 -20
- package/src/core/network/messaging/validators/AdminResponse.js +2 -2
- package/src/core/network/messaging/validators/CustomNodeResponse.js +2 -2
- package/src/core/network/messaging/validators/PartialBootstrapDeployment.js +3 -3
- package/src/core/network/messaging/validators/PartialRoleAccess.js +15 -12
- package/src/core/network/messaging/validators/PartialTransaction.js +9 -10
- package/src/core/network/messaging/validators/PartialTransfer.js +10 -7
- package/src/core/network/messaging/validators/ValidatorResponse.js +2 -2
- package/src/core/network/messaging/validators/base/BaseResponse.js +13 -5
- package/src/core/network/messaging/validators/base/PartialOperation.js +37 -21
- package/src/core/network/services/ConnectionManager.js +9 -15
- package/src/core/network/services/MessageOrchestrator.js +10 -22
- package/src/core/network/services/TransactionPoolService.js +9 -8
- package/src/core/network/services/ValidatorObserverService.js +46 -21
- package/src/core/state/State.js +136 -139
- package/src/core/state/utils/address.js +18 -16
- package/src/core/state/utils/adminEntry.js +17 -16
- package/src/core/state/utils/deploymentEntry.js +15 -15
- package/src/core/state/utils/transaction.js +3 -95
- package/src/index.js +153 -201
- package/src/messages/completeStateMessages/CompleteStateMessageBuilder.js +36 -32
- package/src/messages/completeStateMessages/CompleteStateMessageOperations.js +39 -42
- package/src/messages/partialStateMessages/PartialStateMessageBuilder.js +20 -20
- package/src/messages/partialStateMessages/PartialStateMessageOperations.js +29 -22
- package/src/utils/check.js +21 -17
- package/src/utils/cliCommands.js +11 -11
- package/src/utils/constants.js +2 -10
- package/src/utils/fileUtils.js +1 -4
- package/src/utils/helpers.js +9 -20
- package/src/utils/migrationUtils.js +2 -2
- package/src/utils/normalizers.js +10 -9
- package/tests/acceptance/v1/account/account.test.mjs +2 -2
- package/tests/acceptance/v1/balance/balance.test.mjs +1 -1
- package/tests/acceptance/v1/broadcast-transaction/broadcast-transaction.test.mjs +11 -2
- package/tests/acceptance/v1/rpc.test.mjs +9 -9
- package/tests/acceptance/v1/tx/tx.test.mjs +4 -2
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +7 -3
- package/tests/fixtures/check.fixtures.js +42 -42
- package/tests/fixtures/protobuf.fixtures.js +27 -26
- package/tests/helpers/StateNetworkFactory.js +3 -5
- package/tests/helpers/autobaseTestHelpers.js +1 -2
- package/tests/helpers/config.js +3 -0
- package/tests/helpers/setupApplyTests.js +89 -82
- package/tests/helpers/transactionPayloads.mjs +26 -12
- package/tests/integration/apply/addAdmin/addAdminBasic.test.js +10 -9
- package/tests/integration/apply/addAdmin/addAdminRecovery.test.js +20 -19
- package/tests/integration/apply/addIndexer.test.js +23 -21
- package/tests/integration/apply/addWhitelist.test.js +9 -9
- package/tests/integration/apply/addWriter.test.js +33 -32
- package/tests/integration/apply/banValidator.test.js +16 -9
- package/tests/integration/apply/postTx/invalidSubValues.test.js +4 -4
- package/tests/integration/apply/postTx/postTx.test.js +7 -33
- package/tests/integration/apply/removeIndexer.test.js +11 -7
- package/tests/integration/apply/removeWriter.test.js +20 -19
- package/tests/integration/apply/transfer.test.js +18 -16
- package/tests/unit/messageOperations/assembleAddIndexerMessage.test.js +2 -2
- package/tests/unit/messageOperations/assembleAddWriterMessage.test.js +2 -1
- package/tests/unit/messageOperations/assembleAdminMessage.test.js +9 -10
- package/tests/unit/messageOperations/assembleBanWriterMessage.test.js +3 -2
- package/tests/unit/messageOperations/assemblePostTransaction.test.js +25 -43
- package/tests/unit/messageOperations/assembleRemoveIndexerMessage.test.js +2 -2
- package/tests/unit/messageOperations/assembleRemoveWriterMessage.test.js +2 -2
- package/tests/unit/messageOperations/assembleWhitelistMessages.test.js +5 -4
- package/tests/unit/messageOperations/commonsStateMessageOperationsTest.js +4 -3
- package/tests/unit/network/ConnectionManager.test.js +4 -2
- package/tests/unit/network/NetworkWalletFactory.test.js +14 -14
- package/tests/unit/state/apply/addAdmin/addAdminHappyPathScenario.js +6 -6
- package/tests/unit/state/apply/addAdmin/addAdminScenarioHelpers.js +8 -8
- package/tests/unit/state/apply/addAdmin/state.apply.addAdmin.test.js +6 -5
- package/tests/unit/state/apply/addIndexer/addIndexerScenarioHelpers.js +24 -23
- package/tests/unit/state/apply/addWriter/addWriterScenarioHelpers.js +10 -16
- package/tests/unit/state/apply/addWriter/addWriterValidatorRewardScenario.js +2 -1
- package/tests/unit/state/apply/adminRecovery/adminRecoveryScenarioHelpers.js +45 -41
- package/tests/unit/state/apply/adminRecovery/state.apply.adminRecovery.test.js +3 -7
- package/tests/unit/state/apply/appendWhitelist/appendWhitelistScenarioHelpers.js +17 -16
- package/tests/unit/state/apply/balanceInitialization/balanceInitializationScenarioHelpers.js +3 -4
- package/tests/unit/state/apply/balanceInitialization/nodeEntryBalanceUpdateFailureScenario.js +2 -1
- package/tests/unit/state/apply/banValidator/banValidatorBanAndReWhitelistScenario.js +2 -1
- package/tests/unit/state/apply/banValidator/banValidatorScenarioHelpers.js +23 -25
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentDuplicateRegistrationScenario.js +2 -1
- package/tests/unit/state/apply/bootstrapDeployment/bootstrapDeploymentScenarioHelpers.js +19 -18
- package/tests/unit/state/apply/common/access-control/adminConsistencyMismatchScenario.js +5 -4
- package/tests/unit/state/apply/common/access-control/adminPublicKeyDecodeFailureScenario.js +4 -3
- package/tests/unit/state/apply/common/balances/base/requesterBalanceScenarioBase.js +2 -1
- package/tests/unit/state/apply/common/commonScenarioHelper.js +3 -4
- package/tests/unit/state/apply/common/payload-structure/initializationDisabledScenario.js +2 -2
- package/tests/unit/state/apply/common/payload-structure/invalidHashValidationScenario.js +2 -2
- package/tests/unit/state/apply/common/requester/requesterNodeEntryBufferMissingScenario.js +2 -1
- package/tests/unit/state/apply/common/requester/requesterNodeEntryDecodeFailureScenario.js +2 -1
- package/tests/unit/state/apply/common/validatorConsistency/base/validatorConsistencyScenarioBase.js +2 -1
- package/tests/unit/state/apply/common/validatorEntryValidation/base/validatorEntryValidationScenarioBase.js +2 -1
- package/tests/unit/state/apply/disableInitialization/disableInitializationScenarioHelpers.js +11 -10
- package/tests/unit/state/apply/removeIndexer/removeIndexerScenarioHelpers.js +6 -5
- package/tests/unit/state/apply/removeWriter/removeWriterScenarioHelpers.js +6 -7
- package/tests/unit/state/apply/transfer/transferDoubleSpendAcrossValidatorsScenario.js +35 -34
- package/tests/unit/state/apply/transfer/transferScenarioHelpers.js +44 -43
- package/tests/unit/state/apply/txOperation/txOperationScenarioHelpers.js +26 -25
- package/tests/unit/state/apply/txOperation/txOperationTransferFeeGuardScenarioFactory.js +2 -1
- package/tests/unit/state/stateModule.test.js +0 -1
- package/tests/unit/state/stateTestUtils.js +7 -3
- package/tests/unit/state/utils/address.test.js +3 -3
- package/tests/unit/state/utils/adminEntry.test.js +10 -9
- package/tests/unit/utils/check/adminControlOperation.test.js +3 -3
- package/tests/unit/utils/check/balanceInitializationOperation.test.js +2 -2
- package/tests/unit/utils/check/bootstrapDeploymentOperation.test.js +2 -3
- package/tests/unit/utils/check/common.test.js +7 -6
- package/tests/unit/utils/check/coreAdminOperation.test.js +3 -3
- package/tests/unit/utils/check/roleAccessOperation.test.js +3 -2
- package/tests/unit/utils/check/transactionOperation.test.js +3 -3
- package/tests/unit/utils/check/transferOperation.test.js +3 -3
- package/tests/unit/utils/fileUtils/readAddressesFromWhitelistFile.test.js +2 -1
- package/tests/unit/utils/fileUtils/readBalanceMigrationFile.test.js +2 -1
- package/tests/unit/utils/migrationUtils/validateAddressFromIncomingFile.test.js +7 -0
- package/tests/unit/utils/utils.test.js +0 -1
- package/src/core/state/utils/indexerEntry.js +0 -105
- package/src/utils/crypto.js +0 -11
- package/tests/unit/state/utils/indexerEntry.test.js +0 -83
- package/tests/unit/state/utils/transaction.test.js +0 -97
- package/tests/unit/utils/crypto/createHash.test.js +0 -15
- /package/rpc/{constants.mjs → constants.js} +0 -0
- /package/rpc/{cors.mjs → cors.js} +0 -0
- /package/rpc/utils/{confirmedParameter.mjs → confirmedParameter.js} +0 -0
- /package/rpc/utils/{helpers.mjs → helpers.js} +0 -0
- /package/rpc/utils/{url.mjs → url.js} +0 -0
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
2
|
import PeerWallet from 'trac-wallet';
|
|
3
|
-
|
|
4
3
|
import Check from '../../../../../utils/check.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { FEE } from "../../../../state/utils/transaction.js";
|
|
4
|
+
import {bufferToAddress} from "../../../../state/utils/address.js";
|
|
5
|
+
import {createMessage} from "../../../../../utils/buffer.js";
|
|
6
|
+
import {OperationType} from "../../../../../utils/constants.js";
|
|
7
|
+
import {bufferToBigInt} from "../../../../../utils/amountSerialization.js";
|
|
8
|
+
import {FEE} from "../../../../state/utils/transaction.js";
|
|
11
9
|
import * as operationsUtils from '../../../../../utils/operations.js';
|
|
12
10
|
|
|
13
11
|
const MAX_AMOUNT = BigInt('0xffffffffffffffffffffffffffffffff');
|
|
@@ -17,12 +15,16 @@ const PUBLIC_KEY_LENGTH = 32;
|
|
|
17
15
|
class PartialOperation {
|
|
18
16
|
#state;
|
|
19
17
|
#check;
|
|
18
|
+
#config
|
|
19
|
+
#wallet
|
|
20
20
|
|
|
21
|
-
constructor(state) {
|
|
21
|
+
constructor(state, wallet, config) {
|
|
22
22
|
this.#state = state;
|
|
23
|
-
this.#
|
|
23
|
+
this.#config = config;
|
|
24
|
+
this.#check = new Check(this.#config);
|
|
24
25
|
this.max_amount = MAX_AMOUNT;
|
|
25
26
|
this.fee = FEE_BIGINT;
|
|
27
|
+
this.#wallet = wallet;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
get state() {
|
|
@@ -33,7 +35,9 @@ class PartialOperation {
|
|
|
33
35
|
return this.#check;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
async validate(payload) {
|
|
38
|
+
async validate(payload) {
|
|
39
|
+
throw new Error("Method 'validate()' must be implemented.");
|
|
40
|
+
}
|
|
37
41
|
|
|
38
42
|
isPayloadSchemaValid(payload) {
|
|
39
43
|
if (!payload || !payload.type) {
|
|
@@ -65,7 +69,7 @@ class PartialOperation {
|
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
validateRequesterAddress(payload) {
|
|
68
|
-
const incomingAddress = bufferToAddress(payload.address);
|
|
72
|
+
const incomingAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
69
73
|
if (!incomingAddress) {
|
|
70
74
|
throw new Error('Invalid requesting address in payload.');
|
|
71
75
|
}
|
|
@@ -87,7 +91,7 @@ class PartialOperation {
|
|
|
87
91
|
case OperationType.REMOVE_WRITER:
|
|
88
92
|
case OperationType.ADMIN_RECOVERY:
|
|
89
93
|
return [
|
|
90
|
-
|
|
94
|
+
this.#config.networkId,
|
|
91
95
|
operation.txv,
|
|
92
96
|
operation.iw,
|
|
93
97
|
operation.in,
|
|
@@ -95,7 +99,7 @@ class PartialOperation {
|
|
|
95
99
|
];
|
|
96
100
|
case OperationType.BOOTSTRAP_DEPLOYMENT:
|
|
97
101
|
return [
|
|
98
|
-
|
|
102
|
+
this.#config.networkId,
|
|
99
103
|
operation.txv,
|
|
100
104
|
operation.bs,
|
|
101
105
|
operation.ic,
|
|
@@ -104,7 +108,7 @@ class PartialOperation {
|
|
|
104
108
|
];
|
|
105
109
|
case OperationType.TX:
|
|
106
110
|
return [
|
|
107
|
-
|
|
111
|
+
this.#config.networkId,
|
|
108
112
|
operation.txv,
|
|
109
113
|
operation.iw,
|
|
110
114
|
operation.ch,
|
|
@@ -115,7 +119,7 @@ class PartialOperation {
|
|
|
115
119
|
];
|
|
116
120
|
case OperationType.TRANSFER:
|
|
117
121
|
return [
|
|
118
|
-
|
|
122
|
+
this.#config.networkId,
|
|
119
123
|
operation.txv,
|
|
120
124
|
operation.to,
|
|
121
125
|
operation.am,
|
|
@@ -131,12 +135,12 @@ class PartialOperation {
|
|
|
131
135
|
const operationKey = operationsUtils.operationToPayload(payload.type);
|
|
132
136
|
const operation = payload[operationKey];
|
|
133
137
|
|
|
134
|
-
const incomingPublicKey = PeerWallet.decodeBech32mSafe(bufferToAddress(payload.address));
|
|
138
|
+
const incomingPublicKey = PeerWallet.decodeBech32mSafe(bufferToAddress(payload.address, this.#config.addressPrefix));
|
|
135
139
|
const incomingSignature = operation.is;
|
|
136
140
|
const messageComponents = this.#getMessageComponents(payload);
|
|
137
141
|
|
|
138
142
|
const message = createMessage(...messageComponents);
|
|
139
|
-
const messageHash = await
|
|
143
|
+
const messageHash = await PeerWallet.blake3(message);
|
|
140
144
|
const payloadHash = operation.tx;
|
|
141
145
|
if (!b4a.equals(payloadHash, messageHash)) {
|
|
142
146
|
throw new Error('Regenerated transaction does not match incoming transaction in payload.');
|
|
@@ -172,16 +176,16 @@ class PartialOperation {
|
|
|
172
176
|
isOperationNotCompleted(payload) {
|
|
173
177
|
const operationKey = operationsUtils.operationToPayload(payload.type);
|
|
174
178
|
const operation = payload[operationKey];
|
|
175
|
-
const {
|
|
179
|
+
const {va, vn, vs} = operation;
|
|
176
180
|
|
|
177
|
-
const condition =
|
|
181
|
+
const condition = va === undefined && vn === undefined && vs === undefined
|
|
178
182
|
if (!condition) {
|
|
179
183
|
throw new Error('Transfer operation must not be completed already (va, vn, vs must be undefined).');
|
|
180
184
|
}
|
|
181
185
|
}
|
|
182
186
|
|
|
183
187
|
async validateRequesterBalance(payload, signed = false) {
|
|
184
|
-
const requesterAddress = bufferToAddress(payload.address);
|
|
188
|
+
const requesterAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
185
189
|
let requesterEntry;
|
|
186
190
|
if (signed) {
|
|
187
191
|
requesterEntry = await this.state.getNodeEntry(requesterAddress);
|
|
@@ -204,11 +208,23 @@ class PartialOperation {
|
|
|
204
208
|
const operationKey = operationsUtils.operationToPayload(payload.type);
|
|
205
209
|
const operation = payload[operationKey];
|
|
206
210
|
const bs = operation.bs;
|
|
207
|
-
if (b4a.equals(this.
|
|
211
|
+
if (b4a.equals(this.#config.bootstrap, bs)) {
|
|
208
212
|
throw new Error(`External bootstrap is the same as MSB bootstrap: ${bs.toString('hex')}`);
|
|
209
213
|
}
|
|
210
214
|
}
|
|
211
215
|
|
|
216
|
+
/*
|
|
217
|
+
* Guard against self-validation (RPC/orchestrator loop): a validator may receive its own submitted tx for validation.
|
|
218
|
+
* Even if unlikely, this must be rejected to avoid incorrect failures/punishments.
|
|
219
|
+
* Flow: Validator -> submits tx with tap-wallet -> RPC-> Validator -validates tx-> REJECT (self-validation)
|
|
220
|
+
*/
|
|
221
|
+
validateNoSelfValidation(payload) {
|
|
222
|
+
const requesterAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
223
|
+
if (this.#wallet.address === requesterAddress) {
|
|
224
|
+
throw new Error('Requester address cannot be the same as the validator wallet address.');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
212
228
|
}
|
|
213
229
|
|
|
214
230
|
export default PartialOperation;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { MAX_VALIDATORS_IN_CONNECTION_POOL } from "../../../utils/constants.js"
|
|
2
1
|
import b4a from 'b4a'
|
|
3
2
|
import PeerWallet from "trac-wallet"
|
|
4
|
-
|
|
3
|
+
|
|
5
4
|
|
|
6
5
|
// -- Debug Mode --
|
|
7
6
|
// TODO: Implement a better debug system in the future. This is just temporary.
|
|
@@ -15,14 +14,18 @@ const debugLog = (...args) => {
|
|
|
15
14
|
class ConnectionManager {
|
|
16
15
|
#validators
|
|
17
16
|
#maxValidators
|
|
17
|
+
#config
|
|
18
18
|
|
|
19
19
|
// Note: #validators is using publicKey (Buffer) as key
|
|
20
20
|
// As Buffers are objects, we will rely on internal conversions done by JS to compare them.
|
|
21
21
|
// It would be better to handle these conversions manually by using hex strings as keys to avoid issues
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} config
|
|
24
|
+
**/
|
|
25
|
+
constructor(config) {
|
|
24
26
|
this.#validators = new Map();
|
|
25
|
-
this.#
|
|
27
|
+
this.#config = config
|
|
28
|
+
this.#maxValidators = config.maxValidators
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/**
|
|
@@ -288,19 +291,10 @@ class ConnectionManager {
|
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
293
|
|
|
291
|
-
#evictRandomValidator() {
|
|
292
|
-
const connected = this.connectedValidators();
|
|
293
|
-
if (connected.length === 0) return;
|
|
294
|
-
|
|
295
|
-
const idx = Math.floor(Math.random() * connected.length);
|
|
296
|
-
const toRemove = connected[idx];
|
|
297
|
-
this.remove(toRemove);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
294
|
#toAddress(publicKey) {
|
|
301
295
|
const keyHex = b4a.isBuffer(publicKey) ? publicKey : b4a.from(publicKey, 'hex');
|
|
302
296
|
return PeerWallet.encodeBech32m(
|
|
303
|
-
|
|
297
|
+
this.#config.addressPrefix,
|
|
304
298
|
keyHex
|
|
305
299
|
);
|
|
306
300
|
}
|
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
import { sleep } from '../../../utils/helpers.js';
|
|
2
|
-
import {
|
|
3
|
-
MAX_MESSAGE_SEND_ATTEMPTS,
|
|
4
|
-
MAX_SUCCESSIVE_MESSAGES_PER_VALIDATOR,
|
|
5
|
-
MESSAGE_VALIDATOR_RESPONSE_TIMEOUT_MS,
|
|
6
|
-
MESSAGE_VALIDATOR_RETRY_DELAY_MS
|
|
7
|
-
} from '../../../utils/constants.js';
|
|
8
|
-
|
|
2
|
+
import { operationToPayload } from '../../../utils/operations.js';
|
|
9
3
|
/**
|
|
10
4
|
* MessageOrchestrator coordinates message submission, retry, and validator management.
|
|
11
5
|
* It works with ConnectionManager and ledger state to ensure reliable message delivery.
|
|
12
6
|
*/
|
|
13
7
|
class MessageOrchestrator {
|
|
8
|
+
#config;
|
|
14
9
|
/**
|
|
15
10
|
* Attempts to send a message to validators with retries and state checks.
|
|
16
11
|
* @param {ConnectionManager} connectionManager - The connection manager instance
|
|
17
12
|
* @param {object} state - The state to look for the message outcome
|
|
18
|
-
* @param {object}
|
|
19
|
-
* messageThreshold: How many successful sends before removing a validator from the pool
|
|
20
|
-
* maxRetries: How many times to retry sending a message to a single validator
|
|
21
|
-
* retryDelay: How long to wait between retries (ms)
|
|
22
|
-
* timeout: Overall timeout for sending a message (ms)
|
|
13
|
+
* @param {object} config - Configuration options:
|
|
23
14
|
*/
|
|
24
|
-
constructor(connectionManager, state,
|
|
15
|
+
constructor(connectionManager, state, config) {
|
|
25
16
|
this.connectionManager = connectionManager;
|
|
26
17
|
this.state = state;
|
|
27
|
-
|
|
28
|
-
this.messageThreshold = options.messageThreshold || MAX_SUCCESSIVE_MESSAGES_PER_VALIDATOR;
|
|
29
|
-
this.maxRetries = options.maxRetries || MAX_MESSAGE_SEND_ATTEMPTS; // Amount of retries for a single validator
|
|
30
|
-
this.retryDelay = options.retryDelay || MESSAGE_VALIDATOR_RETRY_DELAY_MS; // How long to wait before retrying (ms)
|
|
31
|
-
this.timeout = options.timeout || MESSAGE_VALIDATOR_RESPONSE_TIMEOUT_MS; // Overall timeout for sending a message (ms)
|
|
18
|
+
this.#config = config;
|
|
32
19
|
}
|
|
33
20
|
|
|
34
21
|
/**
|
|
@@ -38,7 +25,7 @@ class MessageOrchestrator {
|
|
|
38
25
|
*/
|
|
39
26
|
async send(message) {
|
|
40
27
|
const startTime = Date.now();
|
|
41
|
-
while (Date.now() - startTime < this.
|
|
28
|
+
while (Date.now() - startTime < this.#config.messageValidatorResponseTimeout) {
|
|
42
29
|
const validator = this.connectionManager.pickRandomConnectedValidator();
|
|
43
30
|
if (!validator) return false;
|
|
44
31
|
|
|
@@ -52,10 +39,11 @@ class MessageOrchestrator {
|
|
|
52
39
|
|
|
53
40
|
async #attemptSendMessage(validator, message) {
|
|
54
41
|
let attempts = 0;
|
|
55
|
-
|
|
42
|
+
const deductedTxType = operationToPayload(message.type);
|
|
43
|
+
while (attempts <= this.#config.maxRetries) {
|
|
56
44
|
this.connectionManager.sendSingleMessage(message, validator);
|
|
57
45
|
|
|
58
|
-
const appeared = await this.waitForUnsignedState(message.
|
|
46
|
+
const appeared = await this.waitForUnsignedState(message[deductedTxType].tx, this.#config.messageValidatorRetryDelay);
|
|
59
47
|
if (appeared) {
|
|
60
48
|
this.incrementSentCount(validator);
|
|
61
49
|
if (this.shouldRemove(validator)) {
|
|
@@ -88,7 +76,7 @@ class MessageOrchestrator {
|
|
|
88
76
|
}
|
|
89
77
|
|
|
90
78
|
shouldRemove(validatorPubKey) {
|
|
91
|
-
return this.connectionManager.getSentCount(validatorPubKey) >= this.messageThreshold;
|
|
79
|
+
return this.connectionManager.getSentCount(validatorPubKey) >= this.#config.messageThreshold;
|
|
92
80
|
}
|
|
93
81
|
}
|
|
94
82
|
|
|
@@ -5,14 +5,19 @@ import Scheduler from '../../../utils/Scheduler.js';
|
|
|
5
5
|
class TransactionPoolService {
|
|
6
6
|
#state;
|
|
7
7
|
#address;
|
|
8
|
-
#
|
|
8
|
+
#config;
|
|
9
9
|
#tx_pool = [];
|
|
10
10
|
#scheduler = null;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @param {State} state
|
|
14
|
+
* @param {string} address
|
|
15
|
+
* @param {object} config
|
|
16
|
+
**/
|
|
17
|
+
constructor(state, address, config) {
|
|
13
18
|
this.#state = state;
|
|
14
19
|
this.#address = address;
|
|
15
|
-
this.#
|
|
20
|
+
this.#config = config;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
get tx_pool() {
|
|
@@ -27,12 +32,8 @@ class TransactionPoolService {
|
|
|
27
32
|
return this.#address;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
get options() {
|
|
31
|
-
return this.#options;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
35
|
async start() {
|
|
35
|
-
if (!this.
|
|
36
|
+
if (!this.#config.enableWallet) {
|
|
36
37
|
console.info('TransactionPoolService can not start. Wallet is not enabled');
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import PeerWallet from "trac-wallet";
|
|
2
2
|
import b4a from "b4a";
|
|
3
|
-
import { MAX_WRITERS_FOR_ADMIN_INDEXER_CONNECTION
|
|
3
|
+
import { MAX_WRITERS_FOR_ADMIN_INDEXER_CONNECTION } from '../../../utils/constants.js';
|
|
4
4
|
import { bufferToAddress } from '../../state/utils/address.js';
|
|
5
5
|
import { sleep } from '../../../utils/helpers.js';
|
|
6
6
|
import Scheduler from "../../../utils/Scheduler.js";
|
|
7
|
+
import Network from "../Network.js";
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
9
|
+
const DELAY_INTERVAL = 50
|
|
10
|
+
const VALIDATOR_CANDIDATES_PER_CYCLE = 10
|
|
11
|
+
const POLL_INTERVAL = (VALIDATOR_CANDIDATES_PER_CYCLE + 1) * DELAY_INTERVAL // This is to avoid more than one instance of the worker running at the same time
|
|
10
12
|
|
|
11
13
|
// -- Debug Mode --
|
|
12
14
|
// TODO: Implement a better debug system in the future. This is just temporary.
|
|
@@ -18,19 +20,31 @@ const debugLog = (...args) => {
|
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
class ValidatorObserverService {
|
|
21
|
-
#
|
|
23
|
+
#config;
|
|
22
24
|
#state;
|
|
23
25
|
#network;
|
|
24
26
|
#scheduler;
|
|
25
27
|
#address;
|
|
26
28
|
#isInterrupted
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @param {Network} network
|
|
32
|
+
* @param {State} state
|
|
33
|
+
* @param {string} address
|
|
34
|
+
* @param {object} config
|
|
35
|
+
**/
|
|
36
|
+
constructor(network, state, address, config) {
|
|
37
|
+
this.#config = config
|
|
30
38
|
this.#network = network;
|
|
31
39
|
this.#state = state;
|
|
32
40
|
this.#address = address;
|
|
33
41
|
this.#isInterrupted = false;
|
|
42
|
+
if (DEBUG) {
|
|
43
|
+
this.initTimestamp = Date.now();
|
|
44
|
+
this.reachedMax = false;
|
|
45
|
+
this.end = 0;
|
|
46
|
+
this.begin = 0;
|
|
47
|
+
}
|
|
34
48
|
}
|
|
35
49
|
|
|
36
50
|
get state() {
|
|
@@ -65,17 +79,29 @@ class ValidatorObserverService {
|
|
|
65
79
|
|
|
66
80
|
async #worker(next) {
|
|
67
81
|
if (!this.#network.validatorConnectionManager.maxConnectionsReached()) {
|
|
82
|
+
if (DEBUG) this.begin = Date.now();
|
|
68
83
|
const length = await this.#lengthEntry()
|
|
69
84
|
|
|
70
85
|
const promises = [];
|
|
71
|
-
for (let i = 0; i <
|
|
86
|
+
for (let i = 0; i < VALIDATOR_CANDIDATES_PER_CYCLE; i++) {
|
|
72
87
|
promises.push(this.#findValidator(this.#address, length + 1));
|
|
73
88
|
await sleep(DELAY_INTERVAL); // Low key dangerous as the network progresses
|
|
74
89
|
}
|
|
75
90
|
await Promise.all(promises);
|
|
76
91
|
|
|
77
|
-
|
|
92
|
+
if (DEBUG) this.end = Date.now();
|
|
93
|
+
debugLog('Worker cycle completed in (ms):', this.end - this.begin, '| Validator Connections:', this.#network.validatorConnectionManager.connectionCount(), " | Pending: ", this.#network.pendingConnectionsCount());
|
|
94
|
+
}
|
|
95
|
+
else if (DEBUG) {
|
|
96
|
+
if (!this.reachedMax) {
|
|
97
|
+
this.reachedMax = true;
|
|
98
|
+
debugLog('Max validator connections reached. Skipping this cycle.');
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
const elapsed = now - this.initTimestamp;
|
|
101
|
+
debugLog('>>> Time elapsed since start (ms):', elapsed);
|
|
102
|
+
}
|
|
78
103
|
}
|
|
104
|
+
next(POLL_INTERVAL);
|
|
79
105
|
}
|
|
80
106
|
|
|
81
107
|
async #findValidator(address, validatorListLength) {
|
|
@@ -98,30 +124,33 @@ class ValidatorObserverService {
|
|
|
98
124
|
else {
|
|
99
125
|
debugLog(`Found valid validator to connect after ${attempts} attempts.`);
|
|
100
126
|
}
|
|
101
|
-
|
|
127
|
+
|
|
102
128
|
if (!isValidatorValid) return;
|
|
103
|
-
|
|
104
|
-
const validatorAddress = bufferToAddress(validatorAddressBuffer);
|
|
129
|
+
|
|
130
|
+
const validatorAddress = bufferToAddress(validatorAddressBuffer, this.#config.addressPrefix);
|
|
105
131
|
const validatorPubKeyBuffer = PeerWallet.decodeBech32m(validatorAddress);
|
|
106
132
|
const validatorPubKeyHex = validatorPubKeyBuffer.toString('hex');
|
|
107
133
|
const adminEntry = await this.state.getAdminEntry();
|
|
108
134
|
|
|
109
|
-
|
|
110
135
|
if (validatorAddress !== adminEntry?.address || validatorListLength < MAX_WRITERS_FOR_ADMIN_INDEXER_CONNECTION) {
|
|
111
|
-
|
|
136
|
+
this.#network.tryConnect(validatorPubKeyHex, 'validator');
|
|
112
137
|
}
|
|
113
138
|
};
|
|
114
139
|
|
|
115
140
|
async #isValidatorValid(forbiddenAddress, validatorAddressBuffer, validatorListLength) {
|
|
116
|
-
if (validatorAddressBuffer === null || b4a.byteLength(validatorAddressBuffer) !==
|
|
117
|
-
|
|
118
|
-
const validatorAddress = bufferToAddress(validatorAddressBuffer);
|
|
141
|
+
if (validatorAddressBuffer === null || b4a.byteLength(validatorAddressBuffer) !== this.#config.addressLength) return false;
|
|
142
|
+
|
|
143
|
+
const validatorAddress = bufferToAddress(validatorAddressBuffer, this.#config.addressPrefix);
|
|
119
144
|
if (validatorAddress === forbiddenAddress) return false;
|
|
120
145
|
|
|
121
146
|
const validatorPubKeyBuffer = PeerWallet.decodeBech32m(validatorAddress);
|
|
122
147
|
const validatorEntry = await this.state.getNodeEntry(validatorAddress);
|
|
123
148
|
const adminEntry = await this.state.getAdminEntry();
|
|
124
149
|
|
|
150
|
+
if (this.#network.isConnectionPending(validatorPubKeyBuffer.toString('hex'))) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
125
154
|
if (validatorAddress === adminEntry?.address && validatorListLength >= MAX_WRITERS_FOR_ADMIN_INDEXER_CONNECTION) {
|
|
126
155
|
if (this.#network.validatorConnectionManager.exists(validatorPubKeyBuffer)) {
|
|
127
156
|
this.#network.validatorConnectionManager.remove(validatorPubKeyBuffer)
|
|
@@ -146,11 +175,7 @@ class ValidatorObserverService {
|
|
|
146
175
|
}
|
|
147
176
|
|
|
148
177
|
#shouldRun() {
|
|
149
|
-
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return true;
|
|
178
|
+
return this.#config.enableValidatorObserver && !this.#isInterrupted
|
|
154
179
|
}
|
|
155
180
|
|
|
156
181
|
async #lengthEntry() {
|