trac-msb 0.2.9 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/acceptance-tests.yml +7 -11
- package/.github/workflows/lint-pr-title.yml +26 -0
- package/.github/workflows/unit-tests.yml +2 -8
- 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 +8 -9
- 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 +25 -29
- package/rpc/utils/helpers.js +82 -51
- package/src/config/args.js +46 -0
- package/src/config/config.js +78 -5
- package/src/config/env.js +69 -4
- package/src/core/network/Network.js +6 -10
- package/src/core/network/protocols/NetworkMessages.js +1 -1
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +1 -1
- package/src/core/network/protocols/legacy/validators/base/BaseResponse.js +1 -1
- package/src/core/network/protocols/shared/handlers/RoleOperationHandler.js +2 -2
- package/src/core/network/protocols/shared/handlers/SubnetworkOperationHandler.js +1 -1
- package/src/core/network/protocols/shared/handlers/TransferOperationHandler.js +1 -1
- package/src/core/network/protocols/shared/handlers/base/BaseOperationHandler.js +5 -6
- package/src/core/network/services/ConnectionManager.js +1 -1
- package/src/core/network/services/MessageOrchestrator.js +1 -1
- package/src/core/network/services/TransactionPoolService.js +4 -4
- package/src/core/network/services/TransactionRateLimiterService.js +8 -11
- package/src/core/network/services/ValidatorObserverService.js +5 -6
- package/src/core/state/State.js +2 -3
- package/src/index.js +3 -1
- package/src/messages/network/v1/NetworkMessageBuilder.js +2 -2
- package/src/messages/state/ApplyStateMessageBuilder.js +1 -1
- package/src/utils/check.js +1 -1
- package/src/utils/constants.js +0 -17
- package/src/utils/fileUtils.js +13 -0
- package/src/utils/type.js +26 -0
- 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 +27 -16
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +26 -12
- package/tests/fixtures/check.fixtures.js +33 -32
- package/tests/fixtures/networkV1.fixtures.js +3 -2
- 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/unit/messages/network/NetworkMessageBuilder.test.js +3 -3
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +3 -5
- 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/type/type.test.js +25 -0
- package/tests/unit/utils/utils.test.js +1 -0
package/rpc/utils/helpers.js
CHANGED
|
@@ -1,74 +1,105 @@
|
|
|
1
1
|
import b4a from "b4a"
|
|
2
2
|
import { operationToPayload } from "../../src/utils/applyOperations.js"
|
|
3
|
+
import { isHexString } from "../../src/utils/helpers.js";
|
|
4
|
+
|
|
5
|
+
export class ValidationError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "ValidationError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class BroadcastError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "BroadcastError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class NotFoundError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "NotFoundError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
3
26
|
export function decodeBase64Payload(base64) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
27
|
+
let decodedPayloadString
|
|
28
|
+
try {
|
|
29
|
+
decodedPayloadString = b4a.from(base64, "base64").toString("utf-8")
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new ValidationError("Failed to decode base64 payload.")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(decodedPayloadString)
|
|
36
|
+
} catch (err) {
|
|
37
|
+
throw new ValidationError("Decoded payload is not valid JSON.")
|
|
38
|
+
}
|
|
16
39
|
}
|
|
17
40
|
|
|
18
41
|
export function isBase64(str) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
42
|
+
const base64Regex =
|
|
43
|
+
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
|
|
44
|
+
return base64Regex.test(str)
|
|
22
45
|
}
|
|
23
46
|
|
|
24
47
|
export function validatePayloadStructure(payload) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
if (
|
|
49
|
+
typeof payload !== "object" ||
|
|
50
|
+
payload === null ||
|
|
51
|
+
typeof payload.type !== "number" ||
|
|
52
|
+
typeof payload.address !== "string" ||
|
|
53
|
+
!["txo", "tro"].some((key) => key in payload && typeof payload[key] === "object")
|
|
54
|
+
) {
|
|
55
|
+
throw new ValidationError("Invalid payload structure.")
|
|
56
|
+
}
|
|
34
57
|
}
|
|
35
58
|
|
|
36
59
|
export function sanitizeTransferPayload(payload) {
|
|
37
|
-
|
|
60
|
+
const operationKey = operationToPayload(payload.type);
|
|
38
61
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
62
|
+
if (operationKey !== 'tro' && operationKey !== 'txo') {
|
|
63
|
+
throw new ValidationError('Payload is not a transfer/transaction operation.');
|
|
64
|
+
}
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
const operation = payload[operationKey];
|
|
67
|
+
if (payload.address && typeof payload.address === "string") {
|
|
68
|
+
payload.address = payload.address.trim()
|
|
69
|
+
}
|
|
47
70
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
if (operation && typeof operation === "object") {
|
|
72
|
+
for (const [key, value] of Object.entries(operation)) {
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
let sanitized = value.trim()
|
|
52
75
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
76
|
+
// normalize hex-like strings
|
|
77
|
+
if (/^[0-9A-F]+$/i.test(sanitized)) {
|
|
78
|
+
sanitized = sanitized.toLowerCase()
|
|
79
|
+
}
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
payload[operationKey][key] = sanitized
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
62
85
|
|
|
63
|
-
|
|
86
|
+
return payload
|
|
64
87
|
}
|
|
65
88
|
|
|
66
89
|
export function sanitizeBulkPayloadsRequestBody(body) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
const cleanBody = body
|
|
91
|
+
.replace(/^\uFEFF/, '')
|
|
92
|
+
.replace(/\r/g, '')
|
|
93
|
+
.replace(/\0/g, '')
|
|
94
|
+
.trim();
|
|
72
95
|
|
|
73
|
-
|
|
96
|
+
return JSON.parse(cleanBody);
|
|
74
97
|
}
|
|
98
|
+
|
|
99
|
+
export function isValidTxHash(hash) {
|
|
100
|
+
return isHexString(hash) && hash?.length === 64;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function hasSpacesInUrl(url) {
|
|
104
|
+
return url.includes('%20') || url.includes(' ');
|
|
105
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createConfig, ENV } from './env.js';
|
|
2
|
+
|
|
3
|
+
const getArguments = () => {
|
|
4
|
+
const pearApp = typeof Pear !== 'undefined' ? (Pear.app ?? Pear.config) : undefined;
|
|
5
|
+
const runtimeArgs = typeof process !== 'undefined' ? process.argv.slice(2) : [];
|
|
6
|
+
return pearApp?.args ?? runtimeArgs;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const resolveEnvironment = (args = []) => {
|
|
10
|
+
const networkIndex = args.indexOf('--network');
|
|
11
|
+
const network = (networkIndex !== -1 && args[networkIndex + 1]) ? args[networkIndex + 1] : undefined;
|
|
12
|
+
|
|
13
|
+
if (network === ENV.MAINNET) return ENV.MAINNET;
|
|
14
|
+
if (network === ENV.DEVELOPMENT) return ENV.DEVELOPMENT;
|
|
15
|
+
if (network === ENV.TESTNET1 || network === 'testnet') return ENV.TESTNET1;
|
|
16
|
+
return ENV.MAINNET;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const isRpcEnabled = () => {
|
|
20
|
+
const args = getArguments();
|
|
21
|
+
return args.includes('--rpc');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const resolveConfig = () => {
|
|
25
|
+
const args = getArguments();
|
|
26
|
+
const runRpc = isRpcEnabled();
|
|
27
|
+
const selectedEnv = resolveEnvironment(args);
|
|
28
|
+
const storesDirectoryIndex = args.indexOf('--stores-directory');
|
|
29
|
+
const storesDirectory = (storesDirectoryIndex !== -1 && args[storesDirectoryIndex + 1]) ? args[storesDirectoryIndex + 1] : undefined;
|
|
30
|
+
const hostIndex = args.indexOf('--host');
|
|
31
|
+
const host = (hostIndex !== -1 && args[hostIndex + 1]) ? args[hostIndex + 1] : undefined;
|
|
32
|
+
const portIndex = args.indexOf('--port');
|
|
33
|
+
const port = (portIndex !== -1 && args[portIndex + 1]) ? parseInt(args[portIndex + 1], 10) : undefined;
|
|
34
|
+
|
|
35
|
+
const rpc = {
|
|
36
|
+
storesDirectory,
|
|
37
|
+
enableWallet: false,
|
|
38
|
+
enableInteractiveMode: false,
|
|
39
|
+
host,
|
|
40
|
+
port
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const options = runRpc ? rpc : { storesDirectory };
|
|
44
|
+
|
|
45
|
+
return createConfig(selectedEnv, options);
|
|
46
|
+
};
|
package/src/config/config.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import b4a from 'b4a'
|
|
2
|
+
import { isDefined } from '../utils/type.js'
|
|
3
|
+
import _ from 'lodash'
|
|
2
4
|
|
|
3
5
|
export class Config {
|
|
4
6
|
#options
|
|
@@ -80,7 +82,7 @@ export class Config {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
get isAdminMode() {
|
|
83
|
-
return this
|
|
85
|
+
return _.endsWith(this.storesDirectory, '/admin')
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
get keyPairPath() {
|
|
@@ -97,17 +99,73 @@ export class Config {
|
|
|
97
99
|
return this.#config.maxValidators
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
get maxPeers() {
|
|
103
|
+
if (this.#isOverriden('maxPeers')) return this.#options.maxPeers
|
|
104
|
+
return this.#config.maxPeers
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get maxParallel() {
|
|
108
|
+
if (this.#isOverriden('maxParallel')) return this.#options.maxParallel
|
|
109
|
+
return this.#config.maxParallel
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get maxServerConnections() {
|
|
113
|
+
if (this.#isOverriden('maxServerConnections')) return this.#options.maxServerConnections
|
|
114
|
+
return this.#config.maxServerConnections
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get maxClientConnections() {
|
|
118
|
+
if (this.#isOverriden('maxClientConnections')) return this.#options.maxClientConnections
|
|
119
|
+
return this.#config.maxClientConnections
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get maxWritersForAdminIndexerConnection() {
|
|
123
|
+
if (this.#isOverriden('maxWritersForAdminIndexerConnection')) return this.#options.maxWritersForAdminIndexerConnection
|
|
124
|
+
return this.#config.maxWritersForAdminIndexerConnection
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get processIntervalMs() {
|
|
128
|
+
if (this.#isOverriden('processIntervalMs')) return this.#options.processIntervalMs
|
|
129
|
+
return this.#config.processIntervalMs
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get maxPartialTxPayloadByteSize() {
|
|
133
|
+
if (this.#isOverriden('maxPartialTxPayloadByteSize')) return this.#options.maxPartialTxPayloadByteSize
|
|
134
|
+
return this.#config.maxPartialTxPayloadByteSize
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get transactionPoolSize() {
|
|
138
|
+
if (this.#isOverriden('transactionPoolSize')) return this.#options.transactionPoolSize
|
|
139
|
+
return this.#config.transactionPoolSize
|
|
140
|
+
}
|
|
141
|
+
|
|
100
142
|
get networkId() {
|
|
101
143
|
return this.#config.networkId
|
|
102
144
|
}
|
|
103
145
|
|
|
146
|
+
get host() {
|
|
147
|
+
if (this.#isOverriden('host')) return this.#options.host
|
|
148
|
+
return this.#config.host
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
get port() {
|
|
152
|
+
if (this.#isOverriden('port')) return this.#options.port
|
|
153
|
+
return this.#config.port
|
|
154
|
+
}
|
|
155
|
+
|
|
104
156
|
get storesDirectory() {
|
|
105
|
-
|
|
106
|
-
|
|
157
|
+
const storesDirectory = this.#isOverriden('storesDirectory') ?
|
|
158
|
+
this.#options.storesDirectory : this.#config.storesDirectory
|
|
159
|
+
|
|
160
|
+
return _.trimEnd(storesDirectory, '/')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get storeName() {
|
|
164
|
+
return this.#config.storeName
|
|
107
165
|
}
|
|
108
166
|
|
|
109
167
|
get storesFullPath() {
|
|
110
|
-
return `${this.storesDirectory}
|
|
168
|
+
return `${this.storesDirectory}/${this.storeName}`
|
|
111
169
|
}
|
|
112
170
|
|
|
113
171
|
get messageThreshold() {
|
|
@@ -122,9 +180,24 @@ export class Config {
|
|
|
122
180
|
return this.#config.messageValidatorResponseTimeout
|
|
123
181
|
}
|
|
124
182
|
|
|
183
|
+
get rateLimitCleanupIntervalMs() {
|
|
184
|
+
if (this.#isOverriden('rateLimitCleanupIntervalMs')) return this.#options.rateLimitCleanupIntervalMs
|
|
185
|
+
return this.#config.rateLimitCleanupIntervalMs
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
get rateLimitConnectionTimeoutMs() {
|
|
189
|
+
if (this.#isOverriden('rateLimitConnectionTimeoutMs')) return this.#options.rateLimitConnectionTimeoutMs
|
|
190
|
+
return this.#config.rateLimitConnectionTimeoutMs
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
get rateLimitMaxTransactionsPerSecond() {
|
|
194
|
+
if (this.#isOverriden('rateLimitMaxTransactionsPerSecond')) return this.#options.rateLimitMaxTransactionsPerSecond
|
|
195
|
+
return this.#config.rateLimitMaxTransactionsPerSecond
|
|
196
|
+
}
|
|
197
|
+
|
|
125
198
|
// Most of these properties are boolean
|
|
126
199
|
#isOverriden(prop) {
|
|
127
|
-
return this.#options.hasOwnProperty(prop)
|
|
200
|
+
return this.#options.hasOwnProperty(prop) && isDefined(this.#options[prop])
|
|
128
201
|
}
|
|
129
202
|
|
|
130
203
|
#validate(options, config) {
|
package/src/config/env.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { TRAC_NETWORK_MSB_MAINNET_PREFIX } from 'trac-wallet/constants.js';
|
|
1
|
+
import { TRAC_NETWORK_MSB_MAINNET_PREFIX, TRAC_NETWORK_MSB_TESTNET1_PREFIX } from 'trac-wallet/constants.js';
|
|
2
2
|
import { Config } from './config.js';
|
|
3
|
+
import { TRAC_NETWORK_TESTNET_ID, TRAC_NETWORK_MAINNET_ID } from 'trac-crypto-api/constants.js';
|
|
3
4
|
|
|
4
5
|
export const ENV = {
|
|
5
6
|
MAINNET: 'mainnet',
|
|
@@ -7,9 +8,45 @@ export const ENV = {
|
|
|
7
8
|
TESTNET1: 'testnet1'
|
|
8
9
|
}
|
|
9
10
|
// TODO: CREATE TEST ENV CONFIG SIMILAR TO MAINNET AND USE IT IN TESTS.
|
|
10
|
-
// TODO: CREATE TESTNET1 ENV CONFIG and update npm scripts to run node witn mainnet or testnet1.
|
|
11
11
|
|
|
12
12
|
const configData = {
|
|
13
|
+
[ENV.TESTNET1]: {
|
|
14
|
+
addressLength: 67,
|
|
15
|
+
addressPrefix: TRAC_NETWORK_MSB_TESTNET1_PREFIX,
|
|
16
|
+
addressPrefixLength: TRAC_NETWORK_MSB_TESTNET1_PREFIX.length,
|
|
17
|
+
bech32mHrpLength: TRAC_NETWORK_MSB_TESTNET1_PREFIX.length + 1, // len(addressPrefix + separator)
|
|
18
|
+
bootstrap: 'a7c0c2cf8e4722129097f89b2a29a092f23d6268fcca3fd9570ba5b702b99b95',
|
|
19
|
+
channel: '1111trac1network1msb1testnet1111',
|
|
20
|
+
dhtBootstrap: ['116.202.214.149:10001', '157.180.12.214:10001', 'node1.hyperdht.org:49737', 'node2.hyperdht.org:49737', 'node3.hyperdht.org:49737'], // these are used to peer discovery
|
|
21
|
+
disableRateLimit: false,
|
|
22
|
+
enableErrorApplyLogs: true,
|
|
23
|
+
enableInteractiveMode: true,
|
|
24
|
+
enableRoleRequester: false,
|
|
25
|
+
enableTxApplyLogs: true,
|
|
26
|
+
enableValidatorObserver: true,
|
|
27
|
+
enableWallet: true,
|
|
28
|
+
maxValidators: 6,
|
|
29
|
+
maxRetries: 1,
|
|
30
|
+
messageThreshold: 1000,
|
|
31
|
+
messageValidatorRetryDelay: 1000, //How long to wait before retrying (ms) MESSAGE_VALIDATOR_RETRY_DELAY_MS
|
|
32
|
+
messageValidatorResponseTimeout: 3 * 3 * 1000, //Overall timeout for sending a message (ms). This is 3 * maxRetries * messageValidatorRetryDelay;
|
|
33
|
+
host: 'localhost',
|
|
34
|
+
port: 5000,
|
|
35
|
+
networkId: TRAC_NETWORK_TESTNET_ID,
|
|
36
|
+
maxPeers: 64, // Connectivity constants
|
|
37
|
+
maxParallel: 64, // Connectivity constants
|
|
38
|
+
maxServerConnections: Infinity, // Connectivity constants
|
|
39
|
+
maxClientConnections: Infinity, // Connectivity constants
|
|
40
|
+
maxWritersForAdminIndexerConnection: 10, // Connectivity constants
|
|
41
|
+
processIntervalMs: 50, // Pool constants
|
|
42
|
+
maxPartialTxPayloadByteSize: 3072, // Operation handler constants
|
|
43
|
+
transactionPoolSize: 1000, // Operation handler constants
|
|
44
|
+
rateLimitCleanupIntervalMs: 120_000, // Rate limiting constants
|
|
45
|
+
rateLimitConnectionTimeoutMs: 60_000, // Rate limiting constants
|
|
46
|
+
rateLimitMaxTransactionsPerSecond: 50, // Rate limiting constants
|
|
47
|
+
storesDirectory : 'stores/',
|
|
48
|
+
storeName: 'testnet',
|
|
49
|
+
},
|
|
13
50
|
[ENV.MAINNET]: {
|
|
14
51
|
addressLength: 63,
|
|
15
52
|
addressPrefix: TRAC_NETWORK_MSB_MAINNET_PREFIX,
|
|
@@ -30,8 +67,22 @@ const configData = {
|
|
|
30
67
|
messageThreshold: 3,
|
|
31
68
|
messageValidatorRetryDelay: 1000, //How long to wait before retrying (ms) MESSAGE_VALIDATOR_RETRY_DELAY_MS
|
|
32
69
|
messageValidatorResponseTimeout: 3 * 3 * 1000, //Overall timeout for sending a message (ms). This is 3 * maxRetries * messageValidatorRetryDelay;
|
|
33
|
-
|
|
70
|
+
host: 'localhost',
|
|
71
|
+
port: 5000,
|
|
72
|
+
networkId: TRAC_NETWORK_MAINNET_ID,
|
|
73
|
+
maxPeers: 64, // Connectivity constants
|
|
74
|
+
maxParallel: 64, // Connectivity constants
|
|
75
|
+
maxServerConnections: Infinity, // Connectivity constants
|
|
76
|
+
maxClientConnections: Infinity, // Connectivity constants
|
|
77
|
+
maxWritersForAdminIndexerConnection: 10, // Connectivity constants
|
|
78
|
+
processIntervalMs: 50, // Pool constants
|
|
79
|
+
maxPartialTxPayloadByteSize: 3072, // Operation handler constants
|
|
80
|
+
transactionPoolSize: 1000, // Operation handler constants
|
|
81
|
+
rateLimitCleanupIntervalMs: 120_000, // Rate limiting constants
|
|
82
|
+
rateLimitConnectionTimeoutMs: 60_000, // Rate limiting constants
|
|
83
|
+
rateLimitMaxTransactionsPerSecond: 50, // Rate limiting constants
|
|
34
84
|
storesDirectory: 'stores/',
|
|
85
|
+
storeName: 'mainnet',
|
|
35
86
|
},
|
|
36
87
|
[ENV.DEVELOPMENT]: {
|
|
37
88
|
addressLength: 63,
|
|
@@ -53,8 +104,22 @@ const configData = {
|
|
|
53
104
|
messageThreshold: 1000,
|
|
54
105
|
messageValidatorRetryDelay: 1000, //How long to wait before retrying (ms) MESSAGE_VALIDATOR_RETRY_DELAY_MS
|
|
55
106
|
messageValidatorResponseTimeout: 3 * 3 * 1000, //Overall timeout for sending a message (ms). This is 3 * maxRetries * messageValidatorRetryDelay;
|
|
56
|
-
|
|
107
|
+
host: 'localhost',
|
|
108
|
+
port: 5000,
|
|
109
|
+
networkId: TRAC_NETWORK_TESTNET_ID,
|
|
110
|
+
maxPeers: 64, // Connectivity constants
|
|
111
|
+
maxParallel: 64, // Connectivity constants
|
|
112
|
+
maxServerConnections: Infinity, // Connectivity constants
|
|
113
|
+
maxClientConnections: Infinity, // Connectivity constants
|
|
114
|
+
maxWritersForAdminIndexerConnection: 10, // Connectivity constants
|
|
115
|
+
processIntervalMs: 50, // Pool constants
|
|
116
|
+
maxPartialTxPayloadByteSize: 3072, // Operation handler constants
|
|
117
|
+
transactionPoolSize: 1000, // Operation handler constants
|
|
118
|
+
rateLimitCleanupIntervalMs: 120_000, // Rate limiting constants
|
|
119
|
+
rateLimitConnectionTimeoutMs: 60_000, // Rate limiting constants
|
|
120
|
+
rateLimitMaxTransactionsPerSecond: 50, // Rate limiting constants
|
|
57
121
|
storesDirectory : 'stores/',
|
|
122
|
+
storeName: 'development',
|
|
58
123
|
}
|
|
59
124
|
}
|
|
60
125
|
|
|
@@ -8,10 +8,6 @@ import NetworkMessages from './protocols/NetworkMessages.js';
|
|
|
8
8
|
import { sleep } from '../../utils/helpers.js';
|
|
9
9
|
import {
|
|
10
10
|
TRAC_NAMESPACE,
|
|
11
|
-
MAX_PEERS,
|
|
12
|
-
MAX_PARALLEL,
|
|
13
|
-
MAX_SERVER_CONNECTIONS,
|
|
14
|
-
MAX_CLIENT_CONNECTIONS,
|
|
15
11
|
NETWORK_MESSAGE_TYPES
|
|
16
12
|
} from '../../utils/constants.js';
|
|
17
13
|
import ConnectionManager from './services/ConnectionManager.js';
|
|
@@ -47,7 +43,7 @@ class Network extends ReadyResource {
|
|
|
47
43
|
|
|
48
44
|
/**
|
|
49
45
|
* @param {State} state
|
|
50
|
-
* @param {
|
|
46
|
+
* @param {Config} config
|
|
51
47
|
* @param {string} address
|
|
52
48
|
**/
|
|
53
49
|
constructor(state, config, address = null) {
|
|
@@ -154,13 +150,13 @@ class Network extends ReadyResource {
|
|
|
154
150
|
this.#swarm = new Hyperswarm({
|
|
155
151
|
keyPair,
|
|
156
152
|
bootstrap: this.#config.dhtBootstrap,
|
|
157
|
-
maxPeers:
|
|
158
|
-
maxParallel:
|
|
159
|
-
maxServerConnections:
|
|
160
|
-
maxClientConnections:
|
|
153
|
+
maxPeers: this.#config.maxPeers,
|
|
154
|
+
maxParallel: this.#config.maxParallel,
|
|
155
|
+
maxServerConnections: this.#config.maxServerConnections,
|
|
156
|
+
maxClientConnections: this.#config.maxClientConnections
|
|
161
157
|
});
|
|
162
158
|
|
|
163
|
-
this.#rateLimiter = new TransactionRateLimiterService(this.#swarm);
|
|
159
|
+
this.#rateLimiter = new TransactionRateLimiterService(this.#swarm, this.#config);
|
|
164
160
|
this.#networkMessages = new NetworkMessages(
|
|
165
161
|
state,
|
|
166
162
|
wrappedWallet,
|
|
@@ -19,7 +19,7 @@ class NetworkMessageRouter {
|
|
|
19
19
|
* @param {TransactionRateLimiterService} rateLimiterService
|
|
20
20
|
* @param {TransactionPoolService} txPoolService
|
|
21
21
|
* @param {ConnectionManager} connectionManager
|
|
22
|
-
* @param {
|
|
22
|
+
* @param {Config} config
|
|
23
23
|
**/
|
|
24
24
|
constructor(state, wallet, rateLimiterService, txPoolService, connectionManager, config) {
|
|
25
25
|
this.#config = config;
|
|
@@ -16,9 +16,9 @@ class RoleOperationHandler extends BaseOperationHandler {
|
|
|
16
16
|
* @param {PeerWallet} wallet
|
|
17
17
|
* @param {TransactionRateLimiterService} rateLimiter
|
|
18
18
|
* @param {TransactionPoolService} txPoolService
|
|
19
|
-
* @param {
|
|
19
|
+
* @param {Config} config
|
|
20
20
|
**/
|
|
21
|
-
constructor(state, wallet, rateLimiter, txPoolService
|
|
21
|
+
constructor(state, wallet, rateLimiter, txPoolService, config) {
|
|
22
22
|
super(state, wallet, rateLimiter, txPoolService, config);
|
|
23
23
|
this.#wallet = wallet;
|
|
24
24
|
this.#config = config;
|
|
@@ -24,7 +24,7 @@ class SubnetworkOperationHandler extends BaseOperationHandler {
|
|
|
24
24
|
* @param {PeerWallet} wallet
|
|
25
25
|
* @param {TransactionRateLimiterService} rateLimiter
|
|
26
26
|
* @param {TransactionPoolService} txPoolService
|
|
27
|
-
* @param {
|
|
27
|
+
* @param {Config} config
|
|
28
28
|
**/
|
|
29
29
|
constructor( state, wallet, rateLimiter, txPoolService, config) {
|
|
30
30
|
super(state, wallet, rateLimiter, txPoolService, config);
|
|
@@ -15,7 +15,7 @@ class TransferOperationHandler extends BaseOperationHandler {
|
|
|
15
15
|
* @param {State} state
|
|
16
16
|
* @param {PeerWallet} wallet
|
|
17
17
|
* @param {TransactionRateLimiterService} rateLimiter
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {Config} config
|
|
19
19
|
**/
|
|
20
20
|
constructor(state, wallet, rateLimiter, txPoolService, config) {
|
|
21
21
|
super(state, wallet, rateLimiter, txPoolService, config);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
|
-
import {MAX_PARTIAL_TX_PAYLOAD_BYTE_SIZE, TRANSACTION_POOL_SIZE} from '../../../../../../utils/constants.js';
|
|
3
2
|
|
|
4
3
|
class BaseOperationHandler {
|
|
5
4
|
#state;
|
|
@@ -13,7 +12,7 @@ class BaseOperationHandler {
|
|
|
13
12
|
* @param {PeerWallet} wallet
|
|
14
13
|
* @param {TransactionRateLimiterService} rateLimiter
|
|
15
14
|
* @param {TransactionPoolService} txPoolService
|
|
16
|
-
* @param {
|
|
15
|
+
* @param {Config} config
|
|
17
16
|
**/
|
|
18
17
|
constructor(state, wallet, rateLimiter, txPoolService, config) {
|
|
19
18
|
if (new.target === BaseOperationHandler) {
|
|
@@ -30,7 +29,7 @@ class BaseOperationHandler {
|
|
|
30
29
|
// Validate if operation can be processed:
|
|
31
30
|
// - Non-writable nodes cannot process operations
|
|
32
31
|
// - Regular indexers cannot process operations
|
|
33
|
-
// - Admin-indexer can process operations only when network has less than
|
|
32
|
+
// - Admin-indexer can process operations only when network has less than maxWritersForAdminIndexerConnection writers
|
|
34
33
|
const isAllowedToValidate = await this.#state.allowedToValidate(this.#wallet.address);
|
|
35
34
|
const isAdminAllowedToValidate = await this.#state.isAdminAllowedToValidate();
|
|
36
35
|
const canValidate = isAllowedToValidate || isAdminAllowedToValidate;
|
|
@@ -38,12 +37,12 @@ class BaseOperationHandler {
|
|
|
38
37
|
throw new Error('OperationHandler: State is not writable or is an indexer without admin privileges.');
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
if (this.#txPoolService.tx_pool.length >=
|
|
40
|
+
if (this.#txPoolService.tx_pool.length >= this.#config.transactionPoolSize) {
|
|
42
41
|
throw new Error("OperationHandler: Transaction pool is full, ignoring incoming transaction.");
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
if (b4a.byteLength(JSON.stringify(payload)) >
|
|
46
|
-
throw new Error(`OperationHandler: Payload size exceeds maximum limit of ${
|
|
44
|
+
if (b4a.byteLength(JSON.stringify(payload)) > this.#config.maxPartialTxPayloadByteSize) {
|
|
45
|
+
throw new Error(`OperationHandler: Payload size exceeds maximum limit of ${this.#config.maxPartialTxPayloadByteSize} bytes by ${b4a.byteLength(JSON.stringify(payload)) - this.#config.maxPartialTxPayloadByteSize} bytes.`);
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
if (!this.#config.disableRateLimit) {
|
|
@@ -20,7 +20,7 @@ class ConnectionManager {
|
|
|
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
22
|
/**
|
|
23
|
-
* @param {
|
|
23
|
+
* @param {Config} config
|
|
24
24
|
**/
|
|
25
25
|
constructor(config) {
|
|
26
26
|
this.#validators = new Map();
|
|
@@ -10,7 +10,7 @@ class MessageOrchestrator {
|
|
|
10
10
|
* Attempts to send a message to validators with retries and state checks.
|
|
11
11
|
* @param {ConnectionManager} connectionManager - The connection manager instance
|
|
12
12
|
* @param {object} state - The state to look for the message outcome
|
|
13
|
-
* @param {
|
|
13
|
+
* @param {Config} config - Configuration options:
|
|
14
14
|
*/
|
|
15
15
|
constructor(connectionManager, state, config) {
|
|
16
16
|
this.connectionManager = connectionManager;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// PoolService.js
|
|
2
|
-
import { BATCH_SIZE
|
|
2
|
+
import { BATCH_SIZE } from '../../../utils/constants.js';
|
|
3
3
|
import Scheduler from '../../../utils/Scheduler.js';
|
|
4
4
|
|
|
5
5
|
class TransactionPoolService {
|
|
@@ -12,7 +12,7 @@ class TransactionPoolService {
|
|
|
12
12
|
/**
|
|
13
13
|
* @param {State} state
|
|
14
14
|
* @param {string} address
|
|
15
|
-
* @param {
|
|
15
|
+
* @param {Config} config
|
|
16
16
|
**/
|
|
17
17
|
constructor(state, address, config) {
|
|
18
18
|
this.#state = state;
|
|
@@ -52,7 +52,7 @@ class TransactionPoolService {
|
|
|
52
52
|
if (this.#tx_pool.length > 0) {
|
|
53
53
|
next(0);
|
|
54
54
|
} else {
|
|
55
|
-
next(
|
|
55
|
+
next(this.#config.processIntervalMs);
|
|
56
56
|
}
|
|
57
57
|
} catch (error) {
|
|
58
58
|
throw new Error(`TransactionPoolService worker error: ${error.message}`);
|
|
@@ -60,7 +60,7 @@ class TransactionPoolService {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
#createScheduler() {
|
|
63
|
-
return new Scheduler((next) => this.#worker(next),
|
|
63
|
+
return new Scheduler((next) => this.#worker(next), this.#config.processIntervalMs);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async #processTransactions() {
|