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
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import {OperationType} from '../../../../../utils/constants.js';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import PartialRoleAccessValidator from "../../shared/validators/PartialRoleAccessValidator.js";
|
|
3
|
+
import BaseStateOperationHandler from './BaseStateOperationHandler.js';
|
|
4
4
|
import {applyStateMessageFactory} from "../../../../../messages/state/applyStateMessageFactory.js";
|
|
5
5
|
import {safeEncodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
|
|
6
6
|
import {normalizeRoleAccessOperation} from "../../../../../utils/normalizers.js";
|
|
7
|
+
import { publicKeyToAddress } from "../../../../../utils/helpers.js"
|
|
8
|
+
import b4a from "b4a";
|
|
7
9
|
|
|
8
|
-
class
|
|
10
|
+
class LegacyRoleOperationHandler extends BaseStateOperationHandler {
|
|
9
11
|
#partialRoleAccessValidator;
|
|
10
12
|
#wallet;
|
|
11
13
|
#config;
|
|
12
|
-
#txPoolService;
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @param {State} state
|
|
16
17
|
* @param {PeerWallet} wallet
|
|
17
18
|
* @param {TransactionRateLimiterService} rateLimiter
|
|
18
19
|
* @param {TransactionPoolService} txPoolService
|
|
19
|
-
* @param {
|
|
20
|
+
* @param {Config} config
|
|
20
21
|
**/
|
|
21
|
-
constructor(state, wallet, rateLimiter, txPoolService
|
|
22
|
+
constructor(state, wallet, rateLimiter, txPoolService, config) {
|
|
22
23
|
super(state, wallet, rateLimiter, txPoolService, config);
|
|
23
24
|
this.#wallet = wallet;
|
|
24
25
|
this.#config = config;
|
|
25
|
-
this.#partialRoleAccessValidator = new
|
|
26
|
-
this.#txPoolService = txPoolService;
|
|
26
|
+
this.#partialRoleAccessValidator = new PartialRoleAccessValidator(state, this.#wallet.address ,this.#config)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
get partialRoleAccessValidator() {
|
|
@@ -35,7 +35,8 @@ class RoleOperationHandler extends BaseOperationHandler {
|
|
|
35
35
|
const isValid = await this.partialRoleAccessValidator.validate(normalizedPartialRoleAccessPayload)
|
|
36
36
|
let completePayload = null
|
|
37
37
|
if (!isValid) {
|
|
38
|
-
throw new Error(
|
|
38
|
+
throw new Error(
|
|
39
|
+
`OperationHandler: partial role access payload validation failed. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)}`);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
switch (normalizedPartialRoleAccessPayload.type) {
|
|
@@ -74,15 +75,21 @@ class RoleOperationHandler extends BaseOperationHandler {
|
|
|
74
75
|
)
|
|
75
76
|
break;
|
|
76
77
|
default:
|
|
77
|
-
throw new Error(
|
|
78
|
+
throw new Error(
|
|
79
|
+
`OperationHandler: Assembling complete role access operation failed due to unsupported operation type. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)}`
|
|
80
|
+
);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
if (!completePayload) {
|
|
81
|
-
throw new Error(
|
|
84
|
+
throw new Error(
|
|
85
|
+
`OperationHandler: Assembling complete role access operation failed. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)}`
|
|
86
|
+
);
|
|
82
87
|
}
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
const encodedOperation = safeEncodeApplyOperation(completePayload);
|
|
90
|
+
const txHash = b4a.toString(completePayload.rao.tx, 'hex');
|
|
91
|
+
this.enqueueTransaction(txHash, encodedOperation);
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
export default
|
|
95
|
+
export default LegacyRoleOperationHandler;
|
|
@@ -1,47 +1,49 @@
|
|
|
1
|
-
import
|
|
1
|
+
import BaseStateOperationHandler from './BaseStateOperationHandler.js';
|
|
2
2
|
import {
|
|
3
3
|
OperationType
|
|
4
4
|
} from '../../../../../utils/constants.js';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import PartialBootstrapDeploymentValidator from "../../shared/validators/PartialBootstrapDeploymentValidator.js";
|
|
6
|
+
import PartialTransactionValidator from "../../shared/validators/PartialTransactionValidator.js";
|
|
7
7
|
import {applyStateMessageFactory} from "../../../../../messages/state/applyStateMessageFactory.js";
|
|
8
8
|
import {safeEncodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
|
|
9
9
|
import {
|
|
10
10
|
normalizeBootstrapDeploymentOperation,
|
|
11
11
|
normalizeTransactionOperation
|
|
12
12
|
} from "../../../../../utils/normalizers.js";
|
|
13
|
+
import b4a from "b4a";
|
|
14
|
+
import {publicKeyToAddress} from "../../../../../utils/helpers.js";
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
class
|
|
17
|
+
class LegacySubnetworkOperationHandler extends BaseStateOperationHandler {
|
|
16
18
|
#partialBootstrapDeploymentValidator;
|
|
17
19
|
#partialTransactionValidator;
|
|
18
20
|
#config;
|
|
19
21
|
#wallet;
|
|
20
|
-
#txPoolService;
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* @param {State} state
|
|
24
25
|
* @param {PeerWallet} wallet
|
|
25
26
|
* @param {TransactionRateLimiterService} rateLimiter
|
|
26
27
|
* @param {TransactionPoolService} txPoolService
|
|
27
|
-
* @param {
|
|
28
|
+
* @param {Config} config
|
|
28
29
|
**/
|
|
29
|
-
constructor(
|
|
30
|
+
constructor(state, wallet, rateLimiter, txPoolService, config) {
|
|
30
31
|
super(state, wallet, rateLimiter, txPoolService, config);
|
|
31
32
|
this.#config = config;
|
|
32
33
|
this.#wallet = wallet
|
|
33
|
-
this.#partialBootstrapDeploymentValidator = new
|
|
34
|
-
this.#partialTransactionValidator = new
|
|
35
|
-
this.#txPoolService = txPoolService;
|
|
34
|
+
this.#partialBootstrapDeploymentValidator = new PartialBootstrapDeploymentValidator(state, this.#wallet.address, config);
|
|
35
|
+
this.#partialTransactionValidator = new PartialTransactionValidator(state, this.#wallet.address, config);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
async handleOperation(payload, connection)
|
|
38
|
+
async handleOperation(payload, connection) {
|
|
39
39
|
if (payload.type === OperationType.TX) {
|
|
40
40
|
await this.#partialTransactionSubHandler(payload, connection);
|
|
41
41
|
} else if (payload.type === OperationType.BOOTSTRAP_DEPLOYMENT) {
|
|
42
42
|
await this.#partialBootstrapDeploymentSubHandler(payload, connection);
|
|
43
43
|
} else {
|
|
44
|
-
throw new Error(
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Unsupported operation type for LegacySubnetworkOperationHandler. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)} `
|
|
46
|
+
);
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -49,10 +51,12 @@ class SubnetworkOperationHandler extends BaseOperationHandler {
|
|
|
49
51
|
const normalizedPayload = normalizeTransactionOperation(payload, this.#config);
|
|
50
52
|
const isValid = await this.#partialTransactionValidator.validate(normalizedPayload);
|
|
51
53
|
if (!isValid) {
|
|
52
|
-
throw new Error(
|
|
54
|
+
throw new Error(`
|
|
55
|
+
SubnetworkHandler: Transaction validation failed. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)}`
|
|
56
|
+
);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
const completeTransactionOperation = await applyStateMessageFactory(this.#wallet,this.#config)
|
|
59
|
+
const completeTransactionOperation = await applyStateMessageFactory(this.#wallet, this.#config)
|
|
56
60
|
.buildCompleteTransactionOperationMessage(
|
|
57
61
|
normalizedPayload.address,
|
|
58
62
|
normalizedPayload.txo.tx,
|
|
@@ -64,14 +68,19 @@ class SubnetworkOperationHandler extends BaseOperationHandler {
|
|
|
64
68
|
normalizedPayload.txo.bs,
|
|
65
69
|
normalizedPayload.txo.mbs
|
|
66
70
|
)
|
|
67
|
-
|
|
71
|
+
const encodedOperation = safeEncodeApplyOperation(completeTransactionOperation);
|
|
72
|
+
const txHash = b4a.toString(normalizedPayload.txo.tx, 'hex');
|
|
73
|
+
this.enqueueTransaction(txHash, encodedOperation);
|
|
74
|
+
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
async #partialBootstrapDeploymentSubHandler(payload, connection) {
|
|
71
78
|
const normalizedPayload = normalizeBootstrapDeploymentOperation(payload, this.#config);
|
|
72
79
|
const isValid = await this.#partialBootstrapDeploymentValidator.validate(normalizedPayload);
|
|
73
80
|
if (!isValid) {
|
|
74
|
-
throw new Error(
|
|
81
|
+
throw new Error(
|
|
82
|
+
`SubnetworkHandler: Bootstrap deployment validation failed. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)}`
|
|
83
|
+
);
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
|
|
@@ -85,9 +94,11 @@ class SubnetworkOperationHandler extends BaseOperationHandler {
|
|
|
85
94
|
normalizedPayload.bdo.in,
|
|
86
95
|
normalizedPayload.bdo.is
|
|
87
96
|
)
|
|
88
|
-
|
|
97
|
+
const encodedOperation = safeEncodeApplyOperation(completeBootstrapDeploymentOperation);
|
|
98
|
+
const txHash = b4a.toString(normalizedPayload.bdo.tx, 'hex');
|
|
99
|
+
this.enqueueTransaction(txHash, encodedOperation);
|
|
89
100
|
|
|
90
101
|
}
|
|
91
102
|
}
|
|
92
103
|
|
|
93
|
-
export default
|
|
104
|
+
export default LegacySubnetworkOperationHandler;
|
|
@@ -1,33 +1,36 @@
|
|
|
1
|
-
import
|
|
1
|
+
import BaseStateOperationHandler from './BaseStateOperationHandler.js';
|
|
2
2
|
import {OperationType} from '../../../../../utils/constants.js';
|
|
3
|
-
import
|
|
3
|
+
import PartialTransferValidator from "../../shared/validators/PartialTransferValidator.js";
|
|
4
4
|
import {normalizeTransferOperation} from "../../../../../utils/normalizers.js"
|
|
5
5
|
import {applyStateMessageFactory} from "../../../../../messages/state/applyStateMessageFactory.js";
|
|
6
6
|
import {safeEncodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
|
|
7
|
+
import b4a from "b4a";
|
|
8
|
+
import {publicKeyToAddress} from "../../../../../utils/helpers.js";
|
|
7
9
|
|
|
8
|
-
class
|
|
10
|
+
class LegacyTransferOperationHandler extends BaseStateOperationHandler {
|
|
9
11
|
#partialTransferValidator;
|
|
10
12
|
#config;
|
|
11
13
|
#wallet;
|
|
12
|
-
#txPoolService;
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @param {State} state
|
|
16
17
|
* @param {PeerWallet} wallet
|
|
17
18
|
* @param {TransactionRateLimiterService} rateLimiter
|
|
18
|
-
* @param {
|
|
19
|
+
* @param {TransactionPoolService} txPoolService
|
|
20
|
+
* @param {Config} config
|
|
19
21
|
**/
|
|
20
22
|
constructor(state, wallet, rateLimiter, txPoolService, config) {
|
|
21
23
|
super(state, wallet, rateLimiter, txPoolService, config);
|
|
22
24
|
this.#config = config;
|
|
23
25
|
this.#wallet = wallet;
|
|
24
|
-
this.#partialTransferValidator = new
|
|
25
|
-
this.#txPoolService = txPoolService;
|
|
26
|
+
this.#partialTransferValidator = new PartialTransferValidator(state, this.#wallet.address, this.#config);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
async handleOperation(payload, connection) {
|
|
29
30
|
if (payload.type !== OperationType.TRANSFER) {
|
|
30
|
-
throw new Error(
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Unsupported operation type for LegacyTransferOperationHandler. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)}`
|
|
33
|
+
);
|
|
31
34
|
}
|
|
32
35
|
await this.#handleTransfer(payload, connection);
|
|
33
36
|
}
|
|
@@ -36,7 +39,9 @@ class TransferOperationHandler extends BaseOperationHandler {
|
|
|
36
39
|
const normalizedPayload = normalizeTransferOperation(payload, this.#config);
|
|
37
40
|
const isValid = await this.#partialTransferValidator.validate(normalizedPayload);
|
|
38
41
|
if (!isValid) {
|
|
39
|
-
throw new Error(
|
|
42
|
+
throw new Error(
|
|
43
|
+
`TransferHandler: Transfer validation failed. Requested by ${publicKeyToAddress(connection.remotePublicKey, this.#config)}`
|
|
44
|
+
);
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
const completeTransferOperation = await applyStateMessageFactory(this.#wallet, this.#config)
|
|
@@ -49,9 +54,10 @@ class TransferOperationHandler extends BaseOperationHandler {
|
|
|
49
54
|
normalizedPayload.tro.am,
|
|
50
55
|
normalizedPayload.tro.is
|
|
51
56
|
)
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
const encodedOperation = safeEncodeApplyOperation(completeTransferOperation);
|
|
58
|
+
const txHash = b4a.toString(normalizedPayload.tro.tx, 'hex');
|
|
59
|
+
this.enqueueTransaction(txHash, encodedOperation);
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
export default
|
|
63
|
+
export default LegacyTransferOperationHandler;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {V1ProtocolError} from '../../v1/V1ProtocolError.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared validator rejection error.
|
|
5
|
+
*
|
|
6
|
+
* Used by shared validators in `src/core/network/protocols/shared/validators/**` so they can
|
|
7
|
+
* reject remote peer input with a stable `resultCode`.
|
|
8
|
+
*
|
|
9
|
+
* Note: this currently extends `V1ProtocolError` so the v1 protocol can consistently treat shared
|
|
10
|
+
* validator rejections as typed protocol errors. Legacy protocol codepaths remain compatible because
|
|
11
|
+
* this is still an `Error` and preserves `.message`.
|
|
12
|
+
*
|
|
13
|
+
* Default `endConnection = true` because shared-validator rejections are typically triggered
|
|
14
|
+
* by invalid/malicious peer payloads and should terminate the peer connection after responding.
|
|
15
|
+
*/
|
|
16
|
+
export class SharedValidatorRejectionError extends V1ProtocolError {
|
|
17
|
+
/**
|
|
18
|
+
* @param {number} resultCode Stable rejection reason (a `ResultCode` value).
|
|
19
|
+
* @param {string} message Human-readable error message.
|
|
20
|
+
* @param {boolean} [endConnection=true] Whether the transport should end the connection after responding.
|
|
21
|
+
*/
|
|
22
|
+
constructor(resultCode, message, endConnection = true) {
|
|
23
|
+
super(resultCode, message, endConnection);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default SharedValidatorRejectionError;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import PartialOperationValidator from './PartialOperationValidator.js';
|
|
2
|
+
import {ResultCode} from "../../../../../utils/constants.js";
|
|
3
|
+
import SharedValidatorRejectionError from '../errors/SharedValidatorRejectionError.js';
|
|
2
4
|
|
|
3
|
-
class
|
|
5
|
+
class PartialBootstrapDeploymentValidator extends PartialOperationValidator {
|
|
4
6
|
constructor(state, selfAddress , config) {
|
|
5
7
|
super(state, selfAddress , config);
|
|
6
8
|
}
|
|
@@ -26,9 +28,12 @@ class PartialBootstrapDeployment extends PartialOperation {
|
|
|
26
28
|
async validateBootstrapRegistration(payload) {
|
|
27
29
|
const bootstrapString = payload.bdo.bs.toString('hex');
|
|
28
30
|
if (null !== await this.state.getRegisteredBootstrapEntryUnsigned(bootstrapString)) {
|
|
29
|
-
throw new
|
|
31
|
+
throw new SharedValidatorRejectionError(
|
|
32
|
+
ResultCode.BOOTSTRAP_ALREADY_EXISTS,
|
|
33
|
+
`Bootstrap with hash ${bootstrapString} already exists in the state. Bootstrap must be unique.`
|
|
34
|
+
);
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
export default
|
|
39
|
+
export default PartialBootstrapDeploymentValidator;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
2
|
import PeerWallet from 'trac-wallet';
|
|
3
|
-
import Check from '
|
|
4
|
-
import {bufferToAddress} from "
|
|
5
|
-
import {createMessage} from "
|
|
6
|
-
import {OperationType} from "
|
|
7
|
-
import {bufferToBigInt} from "
|
|
8
|
-
import {FEE} from "
|
|
9
|
-
import * as operationsUtils from '
|
|
3
|
+
import Check from '../../../../../utils/check.js';
|
|
4
|
+
import {bufferToAddress} from "../../../../state/utils/address.js";
|
|
5
|
+
import {createMessage} from "../../../../../utils/buffer.js";
|
|
6
|
+
import {OperationType, ResultCode} from "../../../../../utils/constants.js";
|
|
7
|
+
import {bufferToBigInt} from "../../../../../utils/amountSerialization.js";
|
|
8
|
+
import {FEE} from "../../../../state/utils/transaction.js";
|
|
9
|
+
import * as operationsUtils from '../../../../../utils/applyOperations.js';
|
|
10
|
+
import SharedValidatorRejectionError from '../errors/SharedValidatorRejectionError.js';
|
|
10
11
|
|
|
11
12
|
const MAX_AMOUNT = BigInt('0xffffffffffffffffffffffffffffffff');
|
|
12
13
|
const FEE_BIGINT = bufferToBigInt(FEE);
|
|
13
14
|
const PUBLIC_KEY_LENGTH = 32;
|
|
14
15
|
|
|
15
|
-
class
|
|
16
|
+
class PartialOperationValidator {
|
|
16
17
|
#state;
|
|
17
18
|
#check;
|
|
18
19
|
#config
|
|
@@ -36,18 +37,18 @@ class PartialOperation {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
async validate(payload) {
|
|
39
|
-
throw new
|
|
40
|
+
throw new SharedValidatorRejectionError(ResultCode.UNEXPECTED_ERROR, "Method 'validate()' must be implemented.");
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
isPayloadSchemaValid(payload) {
|
|
43
44
|
if (!payload || !payload.type) {
|
|
44
|
-
throw new
|
|
45
|
+
throw new SharedValidatorRejectionError(ResultCode.TX_INVALID_PAYLOAD, 'Payload or payload type is missing.');
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
const selectedValidator = this.#selectCheckSchemaValidator(payload.type);
|
|
48
49
|
const isPayloadValid = selectedValidator(payload);
|
|
49
50
|
if (!isPayloadValid) {
|
|
50
|
-
throw new
|
|
51
|
+
throw new SharedValidatorRejectionError(ResultCode.SCHEMA_VALIDATION_FAILED, 'Payload is invalid.');
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -64,21 +65,24 @@ class PartialOperation {
|
|
|
64
65
|
case OperationType.TRANSFER:
|
|
65
66
|
return this.check.validateTransferOperation.bind(this.check);
|
|
66
67
|
default:
|
|
67
|
-
throw new
|
|
68
|
+
throw new SharedValidatorRejectionError(
|
|
69
|
+
ResultCode.OPERATION_TYPE_UNKNOWN,
|
|
70
|
+
`Unknown operation type: ${type}`
|
|
71
|
+
);
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
validateRequesterAddress(payload) {
|
|
72
76
|
const incomingAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
73
77
|
if (!incomingAddress) {
|
|
74
|
-
throw new
|
|
78
|
+
throw new SharedValidatorRejectionError(ResultCode.REQUESTER_ADDRESS_INVALID, 'Invalid requesting address in payload.');
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
const incomingPublicKey = PeerWallet.decodeBech32mSafe(incomingAddress);
|
|
78
82
|
|
|
79
83
|
// TODO: We can add check if public key belongs to the Ed25519 curve. Validate signature already checks that but it would be amazing to catch it earlier.
|
|
80
84
|
if (!incomingPublicKey || incomingPublicKey.length !== PUBLIC_KEY_LENGTH) {
|
|
81
|
-
throw new
|
|
85
|
+
throw new SharedValidatorRejectionError(ResultCode.REQUESTER_PUBLIC_KEY_INVALID, 'Invalid requesting public key in payload.');
|
|
82
86
|
}
|
|
83
87
|
}
|
|
84
88
|
|
|
@@ -127,7 +131,10 @@ class PartialOperation {
|
|
|
127
131
|
OperationType.TRANSFER
|
|
128
132
|
];
|
|
129
133
|
default:
|
|
130
|
-
throw new
|
|
134
|
+
throw new SharedValidatorRejectionError(
|
|
135
|
+
ResultCode.OPERATION_TYPE_UNKNOWN,
|
|
136
|
+
`Unknown operation type: ${payload.type}`
|
|
137
|
+
);
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
140
|
|
|
@@ -143,11 +150,14 @@ class PartialOperation {
|
|
|
143
150
|
const messageHash = await PeerWallet.blake3(message);
|
|
144
151
|
const payloadHash = operation.tx;
|
|
145
152
|
if (!b4a.equals(payloadHash, messageHash)) {
|
|
146
|
-
throw new
|
|
153
|
+
throw new SharedValidatorRejectionError(
|
|
154
|
+
ResultCode.TX_HASH_MISMATCH,
|
|
155
|
+
'Regenerated transaction does not match incoming transaction in payload.'
|
|
156
|
+
);
|
|
147
157
|
}
|
|
148
158
|
|
|
149
159
|
if (!PeerWallet.verify(incomingSignature, messageHash, incomingPublicKey)) {
|
|
150
|
-
throw new
|
|
160
|
+
throw new SharedValidatorRejectionError(ResultCode.TX_SIGNATURE_INVALID, 'Invalid signature in payload.');
|
|
151
161
|
}
|
|
152
162
|
}
|
|
153
163
|
|
|
@@ -158,7 +168,7 @@ class PartialOperation {
|
|
|
158
168
|
const incomingTxv = operation.txv
|
|
159
169
|
|
|
160
170
|
if (!b4a.equals(currentTxv, incomingTxv)) {
|
|
161
|
-
throw new
|
|
171
|
+
throw new SharedValidatorRejectionError(ResultCode.TX_EXPIRED, 'Transaction has expired.');
|
|
162
172
|
}
|
|
163
173
|
}
|
|
164
174
|
|
|
@@ -169,7 +179,10 @@ class PartialOperation {
|
|
|
169
179
|
const txHex = tx.toString('hex');
|
|
170
180
|
|
|
171
181
|
if (await this.state.get(txHex) !== null) {
|
|
172
|
-
throw new
|
|
182
|
+
throw new SharedValidatorRejectionError(
|
|
183
|
+
ResultCode.TX_ALREADY_EXISTS,
|
|
184
|
+
`Transaction with hash ${txHex} already exists in the state.`
|
|
185
|
+
);
|
|
173
186
|
}
|
|
174
187
|
}
|
|
175
188
|
|
|
@@ -180,7 +193,10 @@ class PartialOperation {
|
|
|
180
193
|
|
|
181
194
|
const condition = va === undefined && vn === undefined && vs === undefined
|
|
182
195
|
if (!condition) {
|
|
183
|
-
throw new
|
|
196
|
+
throw new SharedValidatorRejectionError(
|
|
197
|
+
ResultCode.OPERATION_ALREADY_COMPLETED,
|
|
198
|
+
'Transfer operation must not be completed already (va, vn, vs must be undefined).'
|
|
199
|
+
);
|
|
184
200
|
}
|
|
185
201
|
}
|
|
186
202
|
|
|
@@ -194,12 +210,12 @@ class PartialOperation {
|
|
|
194
210
|
}
|
|
195
211
|
|
|
196
212
|
if (!requesterEntry) {
|
|
197
|
-
throw new
|
|
213
|
+
throw new SharedValidatorRejectionError(ResultCode.REQUESTER_NOT_FOUND, 'Requester address not found in state');
|
|
198
214
|
}
|
|
199
215
|
|
|
200
216
|
const requesterBalance = bufferToBigInt(requesterEntry.balance);
|
|
201
217
|
if (requesterBalance < FEE_BIGINT) {
|
|
202
|
-
throw new
|
|
218
|
+
throw new SharedValidatorRejectionError(ResultCode.INSUFFICIENT_FEE_BALANCE, 'Insufficient balance to cover transaction fee.');
|
|
203
219
|
}
|
|
204
220
|
}
|
|
205
221
|
|
|
@@ -209,7 +225,10 @@ class PartialOperation {
|
|
|
209
225
|
const operation = payload[operationKey];
|
|
210
226
|
const bs = operation.bs;
|
|
211
227
|
if (b4a.equals(this.#config.bootstrap, bs)) {
|
|
212
|
-
throw new
|
|
228
|
+
throw new SharedValidatorRejectionError(
|
|
229
|
+
ResultCode.EXTERNAL_BOOTSTRAP_EQUALS_MSB_BOOTSTRAP,
|
|
230
|
+
`External bootstrap is the same as MSB bootstrap: ${bs.toString('hex')}`
|
|
231
|
+
);
|
|
213
232
|
}
|
|
214
233
|
}
|
|
215
234
|
|
|
@@ -223,10 +242,13 @@ class PartialOperation {
|
|
|
223
242
|
|
|
224
243
|
const requesterAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
225
244
|
if (this.#selfAddress === requesterAddress) {
|
|
226
|
-
throw new
|
|
245
|
+
throw new SharedValidatorRejectionError(
|
|
246
|
+
ResultCode.SELF_VALIDATION_FORBIDDEN,
|
|
247
|
+
'Requester address cannot be the same as the validator wallet address.'
|
|
248
|
+
);
|
|
227
249
|
}
|
|
228
250
|
}
|
|
229
251
|
|
|
230
252
|
}
|
|
231
253
|
|
|
232
|
-
export default
|
|
254
|
+
export default PartialOperationValidator;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
|
-
import {OperationType} from "../../../../../utils/constants.js";
|
|
2
|
+
import {OperationType, ResultCode} from "../../../../../utils/constants.js";
|
|
3
3
|
import {bufferToAddress} from "../../../../state/utils/address.js";
|
|
4
|
-
import
|
|
4
|
+
import PartialOperationValidator from './PartialOperationValidator.js';
|
|
5
5
|
import {bufferToBigInt} from "../../../../../utils/amountSerialization.js";
|
|
6
|
+
import SharedValidatorRejectionError from '../errors/SharedValidatorRejectionError.js';
|
|
6
7
|
|
|
7
|
-
class
|
|
8
|
+
class PartialRoleAccessValidator extends PartialOperationValidator {
|
|
8
9
|
#config;
|
|
9
10
|
|
|
10
11
|
constructor(state, selfAddress, config) {
|
|
@@ -42,17 +43,26 @@ class PartialRoleAccess extends PartialOperation {
|
|
|
42
43
|
const nodeAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
43
44
|
const nodeEntry = await this.state.getNodeEntry(nodeAddress);
|
|
44
45
|
if (!nodeEntry) {
|
|
45
|
-
throw new
|
|
46
|
+
throw new SharedValidatorRejectionError(
|
|
47
|
+
ResultCode.ROLE_NODE_ENTRY_NOT_FOUND,
|
|
48
|
+
`Node with address ${nodeAddress} entry does not exist.`
|
|
49
|
+
);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
const isNodeAlreadyWriter = nodeEntry.isWriter;
|
|
49
53
|
if (isNodeAlreadyWriter) {
|
|
50
|
-
throw new
|
|
54
|
+
throw new SharedValidatorRejectionError(
|
|
55
|
+
ResultCode.ROLE_NODE_ALREADY_WRITER,
|
|
56
|
+
`Node with address ${nodeAddress} is already a writer.`
|
|
57
|
+
);
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
const isNodeWhitelisted = nodeEntry.isWhitelisted;
|
|
54
61
|
if (!isNodeWhitelisted) {
|
|
55
|
-
throw new
|
|
62
|
+
throw new SharedValidatorRejectionError(
|
|
63
|
+
ResultCode.ROLE_NODE_NOT_WHITELISTED,
|
|
64
|
+
`Node with address ${nodeAddress} is not whitelisted.`
|
|
65
|
+
);
|
|
56
66
|
}
|
|
57
67
|
return;
|
|
58
68
|
|
|
@@ -60,24 +70,33 @@ class PartialRoleAccess extends PartialOperation {
|
|
|
60
70
|
const nodeAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
61
71
|
const nodeEntry = await this.state.getNodeEntry(nodeAddress);
|
|
62
72
|
if (!nodeEntry) {
|
|
63
|
-
throw new
|
|
73
|
+
throw new SharedValidatorRejectionError(
|
|
74
|
+
ResultCode.ROLE_NODE_ENTRY_NOT_FOUND,
|
|
75
|
+
`Node with address ${nodeAddress} entry does not exist.`
|
|
76
|
+
);
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
const isAlreadyWriter = nodeEntry.isWriter;
|
|
67
80
|
if (!isAlreadyWriter) {
|
|
68
|
-
throw new
|
|
81
|
+
throw new SharedValidatorRejectionError(
|
|
82
|
+
ResultCode.ROLE_NODE_NOT_WRITER,
|
|
83
|
+
`Node with address ${nodeAddress} is not a writer.`
|
|
84
|
+
);
|
|
69
85
|
}
|
|
70
86
|
|
|
71
87
|
const isAlreadyIndexer = nodeEntry.isIndexer;
|
|
72
88
|
if (isAlreadyIndexer) {
|
|
73
|
-
throw new
|
|
89
|
+
throw new SharedValidatorRejectionError(
|
|
90
|
+
ResultCode.ROLE_NODE_IS_INDEXER,
|
|
91
|
+
`Node with address ${nodeAddress} is an indexer.`
|
|
92
|
+
);
|
|
74
93
|
}
|
|
75
94
|
return;
|
|
76
95
|
|
|
77
96
|
} else if (type === OperationType.ADMIN_RECOVERY) {
|
|
78
97
|
const adminEntry = await this.state.getAdminEntry();
|
|
79
98
|
if (!adminEntry) {
|
|
80
|
-
throw new
|
|
99
|
+
throw new SharedValidatorRejectionError(ResultCode.ROLE_ADMIN_ENTRY_MISSING, 'Admin entry does not exist.');
|
|
81
100
|
}
|
|
82
101
|
|
|
83
102
|
const adminAddressBuffer = payload.address;
|
|
@@ -87,20 +106,29 @@ class PartialRoleAccess extends PartialOperation {
|
|
|
87
106
|
!b4a.equals(payload.rao.iw, adminEntry.wk)
|
|
88
107
|
);
|
|
89
108
|
if (!isRecoveryCase) {
|
|
90
|
-
throw new
|
|
109
|
+
throw new SharedValidatorRejectionError(
|
|
110
|
+
ResultCode.ROLE_INVALID_RECOVERY_CASE,
|
|
111
|
+
`Node with address ${adminAddress} is not a valid recovery case.`
|
|
112
|
+
);
|
|
91
113
|
}
|
|
92
114
|
|
|
93
115
|
return;
|
|
94
116
|
}
|
|
95
117
|
|
|
96
|
-
throw new
|
|
118
|
+
throw new SharedValidatorRejectionError(
|
|
119
|
+
ResultCode.ROLE_UNKNOWN_OPERATION,
|
|
120
|
+
`Unknown role access operation type: ${type}`
|
|
121
|
+
);
|
|
97
122
|
}
|
|
98
123
|
|
|
99
124
|
async validateWriterKey(payload) {
|
|
100
125
|
const requesterAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
101
126
|
const nodeEntry = await this.state.getNodeEntry(requesterAddress);
|
|
102
127
|
if (!nodeEntry) {
|
|
103
|
-
throw new
|
|
128
|
+
throw new SharedValidatorRejectionError(
|
|
129
|
+
ResultCode.REQUESTER_NOT_FOUND,
|
|
130
|
+
`Node entry not found for address ${requesterAddress}`
|
|
131
|
+
);
|
|
104
132
|
}
|
|
105
133
|
|
|
106
134
|
const writerKey = payload.rao.iw.toString('hex');
|
|
@@ -111,7 +139,10 @@ class PartialRoleAccess extends PartialOperation {
|
|
|
111
139
|
const isOwner = b4a.equals(addressFromRegisteredWritingKey, payload.address);
|
|
112
140
|
|
|
113
141
|
if (!isCurrentWk || !isOwner) {
|
|
114
|
-
throw new
|
|
142
|
+
throw new SharedValidatorRejectionError(
|
|
143
|
+
ResultCode.ROLE_INVALID_WRITER_KEY,
|
|
144
|
+
'Invalid writer key: either not owned by requester or different from assigned key'
|
|
145
|
+
);
|
|
115
146
|
}
|
|
116
147
|
}
|
|
117
148
|
}
|
|
@@ -126,15 +157,18 @@ class PartialRoleAccess extends PartialOperation {
|
|
|
126
157
|
}
|
|
127
158
|
|
|
128
159
|
if (!requesterEntry) {
|
|
129
|
-
throw new
|
|
160
|
+
throw new SharedValidatorRejectionError(ResultCode.REQUESTER_NOT_FOUND, 'Requester address not found in state');
|
|
130
161
|
}
|
|
131
162
|
const requesterBalance = bufferToBigInt(requesterEntry.balance);
|
|
132
163
|
|
|
133
164
|
const requiredBalance = this.fee * 11n;
|
|
134
165
|
if (requesterBalance < requiredBalance) {
|
|
135
|
-
throw new
|
|
166
|
+
throw new SharedValidatorRejectionError(
|
|
167
|
+
ResultCode.ROLE_INSUFFICIENT_FEE_BALANCE,
|
|
168
|
+
'Insufficient requester balance to cover role access operation FEE.'
|
|
169
|
+
);
|
|
136
170
|
}
|
|
137
171
|
}
|
|
138
172
|
}
|
|
139
173
|
|
|
140
|
-
export default
|
|
174
|
+
export default PartialRoleAccessValidator;
|