trac-msb 0.2.11 → 0.2.12
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 +38 -0
- package/.github/workflows/lint-pr-title.yml +26 -0
- package/.github/workflows/publish.yml +33 -0
- package/.github/workflows/unit-tests.yml +34 -0
- package/package.json +5 -10
- package/proto/network.proto +74 -0
- package/rpc/rpc_services.js +4 -22
- package/scripts/generate-protobufs.js +12 -37
- package/src/config/config.js +5 -26
- package/src/config/env.js +11 -25
- package/src/core/network/Network.js +36 -73
- package/src/core/network/protocols/LegacyProtocol.js +11 -21
- package/src/core/network/protocols/NetworkMessages.js +17 -38
- package/src/core/network/protocols/ProtocolInterface.js +2 -14
- package/src/core/network/protocols/ProtocolSession.js +17 -144
- package/src/core/network/protocols/V1Protocol.js +18 -37
- package/src/core/network/protocols/legacy/NetworkMessageRouter.js +19 -25
- package/src/core/network/protocols/legacy/handlers/{LegacyGetRequestHandler.js → GetRequestHandler.js} +6 -6
- package/src/core/network/protocols/legacy/handlers/ResponseHandler.js +37 -0
- package/src/core/network/protocols/{legacy/handlers/LegacyRoleOperationHandler.js → shared/handlers/RoleOperationHandler.js} +11 -18
- package/src/core/network/protocols/{legacy/handlers/LegacySubnetworkOperationHandler.js → shared/handlers/SubnetworkOperationHandler.js} +17 -28
- package/src/core/network/protocols/{legacy/handlers/LegacyTransferOperationHandler.js → shared/handlers/TransferOperationHandler.js} +11 -17
- package/src/core/network/protocols/{legacy/handlers/BaseStateOperationHandler.js → shared/handlers/base/BaseOperationHandler.js} +12 -23
- package/src/core/network/protocols/shared/validators/{PartialBootstrapDeploymentValidator.js → PartialBootstrapDeployment.js} +4 -9
- package/src/core/network/protocols/shared/validators/{PartialRoleAccessValidator.js → PartialRoleAccess.js} +17 -51
- package/src/core/network/protocols/shared/validators/{PartialTransactionValidator.js → PartialTransaction.js} +7 -21
- package/src/core/network/protocols/shared/validators/{PartialTransferValidator.js → PartialTransfer.js} +9 -26
- package/src/core/network/protocols/shared/validators/{PartialOperationValidator.js → base/PartialOperation.js} +25 -47
- package/src/core/network/protocols/v1/NetworkMessageRouter.js +7 -91
- package/src/core/network/services/ConnectionManager.js +94 -146
- package/src/core/network/services/MessageOrchestrator.js +27 -151
- package/src/core/network/services/TransactionPoolService.js +18 -129
- package/src/core/network/services/TransactionRateLimiterService.js +34 -52
- package/src/core/network/services/ValidatorObserverService.js +26 -18
- package/src/core/state/State.js +19 -70
- package/src/index.js +4 -5
- package/src/messages/network/v1/NetworkMessageBuilder.js +79 -59
- package/src/messages/network/v1/NetworkMessageDirector.js +50 -16
- package/src/utils/Scheduler.js +8 -0
- package/src/utils/constants.js +5 -71
- package/src/utils/helpers.js +1 -10
- package/src/utils/normalizers.js +0 -38
- package/src/utils/protobuf/network.cjs +840 -0
- package/src/utils/protobuf/operationHelpers.js +3 -24
- package/tests/acceptance/v1/account/account.test.mjs +2 -8
- package/tests/acceptance/v1/tx/tx.test.mjs +1 -23
- package/tests/acceptance/v1/tx-details/tx-details.test.mjs +6 -34
- package/tests/fixtures/networkV1.fixtures.js +28 -2
- package/tests/helpers/transactionPayloads.mjs +2 -2
- package/tests/unit/messages/network/NetworkMessageBuilder.test.js +79 -239
- package/tests/unit/messages/network/NetworkMessageDirector.test.js +77 -223
- package/tests/unit/network/ConnectionManager.test.js +191 -0
- package/tests/unit/network/networkModule.test.js +1 -4
- package/tests/unit/unit.test.js +2 -2
- package/tests/unit/utils/protobuf/operationHelpers.test.js +4 -2
- package/tests/unit/utils/utils.test.js +0 -1
- package/proto/network/v1/enums/message_type.proto +0 -16
- package/proto/network/v1/enums/result_code.proto +0 -84
- package/proto/network/v1/messages/broadcast_transaction_request.proto +0 -9
- package/proto/network/v1/messages/broadcast_transaction_response.proto +0 -13
- package/proto/network/v1/messages/liveness_request.proto +0 -8
- package/proto/network/v1/messages/liveness_response.proto +0 -11
- package/proto/network/v1/network_message.proto +0 -22
- package/src/core/network/protocols/connectionPolicies.js +0 -88
- package/src/core/network/protocols/legacy/handlers/LegacyResponseHandler.js +0 -23
- package/src/core/network/protocols/shared/errors/SharedValidatorRejectionError.js +0 -27
- package/src/core/network/protocols/v1/V1ProtocolError.js +0 -91
- package/src/core/network/protocols/v1/handlers/V1BaseOperationHandler.js +0 -65
- package/src/core/network/protocols/v1/handlers/V1BroadcastTransactionOperationHandler.js +0 -389
- package/src/core/network/protocols/v1/handlers/V1LivenessOperationHandler.js +0 -87
- package/src/core/network/protocols/v1/validators/V1BaseOperation.js +0 -211
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionRequest.js +0 -26
- package/src/core/network/protocols/v1/validators/V1BroadcastTransactionResponse.js +0 -276
- package/src/core/network/protocols/v1/validators/V1LivenessRequest.js +0 -15
- package/src/core/network/protocols/v1/validators/V1LivenessResponse.js +0 -17
- package/src/core/network/protocols/v1/validators/V1ValidationSchema.js +0 -210
- package/src/core/network/services/PendingRequestService.js +0 -172
- package/src/core/network/services/TransactionCommitService.js +0 -149
- package/src/core/network/services/ValidatorHealthCheckService.js +0 -127
- package/src/utils/deepEqualApplyPayload.js +0 -40
- package/src/utils/logger.js +0 -25
- package/src/utils/protobuf/networkV1.generated.cjs +0 -2460
- package/tests/unit/network/LegacyNetworkMessageRouter.test.js +0 -54
- package/tests/unit/network/ProtocolSession.test.js +0 -127
- package/tests/unit/network/services/ConnectionManager.test.js +0 -450
- package/tests/unit/network/services/MessageOrchestrator.test.js +0 -445
- package/tests/unit/network/services/PendingRequestService.test.js +0 -431
- package/tests/unit/network/services/TransactionCommitService.test.js +0 -246
- package/tests/unit/network/services/TransactionPoolService.test.js +0 -489
- package/tests/unit/network/services/TransactionRateLimiterService.test.js +0 -139
- package/tests/unit/network/services/ValidatorHealthCheckService.test.js +0 -115
- package/tests/unit/network/services/services.test.js +0 -17
- package/tests/unit/network/utils/v1TestUtils.js +0 -153
- package/tests/unit/network/v1/NetworkMessageRouterV1.test.js +0 -151
- package/tests/unit/network/v1/V1BaseOperation.test.js +0 -356
- package/tests/unit/network/v1/V1BroadcastTransactionOperationHandler.test.js +0 -129
- package/tests/unit/network/v1/V1BroadcastTransactionRequest.test.js +0 -53
- package/tests/unit/network/v1/V1BroadcastTransactionResponse.test.js +0 -512
- package/tests/unit/network/v1/V1LivenessRequest.test.js +0 -32
- package/tests/unit/network/v1/V1LivenessResponse.test.js +0 -45
- package/tests/unit/network/v1/V1ResultCode.test.js +0 -84
- package/tests/unit/network/v1/V1ValidationSchema.test.js +0 -13
- package/tests/unit/network/v1/connectionPolicies.test.js +0 -49
- package/tests/unit/network/v1/handlers/V1BaseOperationHandler.test.js +0 -284
- package/tests/unit/network/v1/handlers/V1BroadcastTransactionOperationHandler.test.js +0 -794
- package/tests/unit/network/v1/handlers/V1LivenessOperationHandler.test.js +0 -193
- package/tests/unit/network/v1/v1.handlers.test.js +0 -15
- package/tests/unit/network/v1/v1.test.js +0 -19
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionRequest.test.js +0 -119
- package/tests/unit/network/v1/v1ValidationSchema/broadcastTransactionResponse.test.js +0 -136
- package/tests/unit/network/v1/v1ValidationSchema/common.test.js +0 -308
- package/tests/unit/network/v1/v1ValidationSchema/livenessRequest.test.js +0 -90
- package/tests/unit/network/v1/v1ValidationSchema/livenessResponse.test.js +0 -133
- package/tests/unit/utils/deepEqualApplyPayload/deepEqualApplyPayload.test.js +0 -102
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
|
-
import {OperationType
|
|
2
|
+
import {OperationType} from "../../../../../utils/constants.js";
|
|
3
3
|
import {bufferToAddress} from "../../../../state/utils/address.js";
|
|
4
|
-
import
|
|
4
|
+
import PartialOperation from './base/PartialOperation.js';
|
|
5
5
|
import {bufferToBigInt} from "../../../../../utils/amountSerialization.js";
|
|
6
|
-
import SharedValidatorRejectionError from '../errors/SharedValidatorRejectionError.js';
|
|
7
6
|
|
|
8
|
-
class
|
|
7
|
+
class PartialRoleAccess extends PartialOperation {
|
|
9
8
|
#config;
|
|
10
9
|
|
|
11
10
|
constructor(state, selfAddress, config) {
|
|
@@ -43,26 +42,17 @@ class PartialRoleAccessValidator extends PartialOperationValidator {
|
|
|
43
42
|
const nodeAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
44
43
|
const nodeEntry = await this.state.getNodeEntry(nodeAddress);
|
|
45
44
|
if (!nodeEntry) {
|
|
46
|
-
throw new
|
|
47
|
-
ResultCode.ROLE_NODE_ENTRY_NOT_FOUND,
|
|
48
|
-
`Node with address ${nodeAddress} entry does not exist.`
|
|
49
|
-
);
|
|
45
|
+
throw new Error(`Node with address ${nodeAddress} entry does not exist.`);
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
const isNodeAlreadyWriter = nodeEntry.isWriter;
|
|
53
49
|
if (isNodeAlreadyWriter) {
|
|
54
|
-
throw new
|
|
55
|
-
ResultCode.ROLE_NODE_ALREADY_WRITER,
|
|
56
|
-
`Node with address ${nodeAddress} is already a writer.`
|
|
57
|
-
);
|
|
50
|
+
throw new Error(`Node with address ${nodeAddress} is already a writer.`);
|
|
58
51
|
}
|
|
59
52
|
|
|
60
53
|
const isNodeWhitelisted = nodeEntry.isWhitelisted;
|
|
61
54
|
if (!isNodeWhitelisted) {
|
|
62
|
-
throw new
|
|
63
|
-
ResultCode.ROLE_NODE_NOT_WHITELISTED,
|
|
64
|
-
`Node with address ${nodeAddress} is not whitelisted.`
|
|
65
|
-
);
|
|
55
|
+
throw new Error(`Node with address ${nodeAddress} is not whitelisted.`);
|
|
66
56
|
}
|
|
67
57
|
return;
|
|
68
58
|
|
|
@@ -70,33 +60,24 @@ class PartialRoleAccessValidator extends PartialOperationValidator {
|
|
|
70
60
|
const nodeAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
71
61
|
const nodeEntry = await this.state.getNodeEntry(nodeAddress);
|
|
72
62
|
if (!nodeEntry) {
|
|
73
|
-
throw new
|
|
74
|
-
ResultCode.ROLE_NODE_ENTRY_NOT_FOUND,
|
|
75
|
-
`Node with address ${nodeAddress} entry does not exist.`
|
|
76
|
-
);
|
|
63
|
+
throw new Error(`Node with address ${nodeAddress} entry does not exist.`);
|
|
77
64
|
}
|
|
78
65
|
|
|
79
66
|
const isAlreadyWriter = nodeEntry.isWriter;
|
|
80
67
|
if (!isAlreadyWriter) {
|
|
81
|
-
throw new
|
|
82
|
-
ResultCode.ROLE_NODE_NOT_WRITER,
|
|
83
|
-
`Node with address ${nodeAddress} is not a writer.`
|
|
84
|
-
);
|
|
68
|
+
throw new Error(`Node with address ${nodeAddress} is not a writer.`);
|
|
85
69
|
}
|
|
86
70
|
|
|
87
71
|
const isAlreadyIndexer = nodeEntry.isIndexer;
|
|
88
72
|
if (isAlreadyIndexer) {
|
|
89
|
-
throw new
|
|
90
|
-
ResultCode.ROLE_NODE_IS_INDEXER,
|
|
91
|
-
`Node with address ${nodeAddress} is an indexer.`
|
|
92
|
-
);
|
|
73
|
+
throw new Error(`Node with address ${nodeAddress} is an indexer.`);
|
|
93
74
|
}
|
|
94
75
|
return;
|
|
95
76
|
|
|
96
77
|
} else if (type === OperationType.ADMIN_RECOVERY) {
|
|
97
78
|
const adminEntry = await this.state.getAdminEntry();
|
|
98
79
|
if (!adminEntry) {
|
|
99
|
-
throw new
|
|
80
|
+
throw new Error('Admin entry does not exist.');
|
|
100
81
|
}
|
|
101
82
|
|
|
102
83
|
const adminAddressBuffer = payload.address;
|
|
@@ -106,29 +87,20 @@ class PartialRoleAccessValidator extends PartialOperationValidator {
|
|
|
106
87
|
!b4a.equals(payload.rao.iw, adminEntry.wk)
|
|
107
88
|
);
|
|
108
89
|
if (!isRecoveryCase) {
|
|
109
|
-
throw new
|
|
110
|
-
ResultCode.ROLE_INVALID_RECOVERY_CASE,
|
|
111
|
-
`Node with address ${adminAddress} is not a valid recovery case.`
|
|
112
|
-
);
|
|
90
|
+
throw new Error(`Node with address ${adminAddress} is not a valid recovery case.`);
|
|
113
91
|
}
|
|
114
92
|
|
|
115
93
|
return;
|
|
116
94
|
}
|
|
117
95
|
|
|
118
|
-
throw new
|
|
119
|
-
ResultCode.ROLE_UNKNOWN_OPERATION,
|
|
120
|
-
`Unknown role access operation type: ${type}`
|
|
121
|
-
);
|
|
96
|
+
throw new Error(`Unknown role access operation type: ${type}`);
|
|
122
97
|
}
|
|
123
98
|
|
|
124
99
|
async validateWriterKey(payload) {
|
|
125
100
|
const requesterAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
126
101
|
const nodeEntry = await this.state.getNodeEntry(requesterAddress);
|
|
127
102
|
if (!nodeEntry) {
|
|
128
|
-
throw new
|
|
129
|
-
ResultCode.REQUESTER_NOT_FOUND,
|
|
130
|
-
`Node entry not found for address ${requesterAddress}`
|
|
131
|
-
);
|
|
103
|
+
throw new Error(`Node entry not found for address ${requesterAddress}`);
|
|
132
104
|
}
|
|
133
105
|
|
|
134
106
|
const writerKey = payload.rao.iw.toString('hex');
|
|
@@ -139,10 +111,7 @@ class PartialRoleAccessValidator extends PartialOperationValidator {
|
|
|
139
111
|
const isOwner = b4a.equals(addressFromRegisteredWritingKey, payload.address);
|
|
140
112
|
|
|
141
113
|
if (!isCurrentWk || !isOwner) {
|
|
142
|
-
throw new
|
|
143
|
-
ResultCode.ROLE_INVALID_WRITER_KEY,
|
|
144
|
-
'Invalid writer key: either not owned by requester or different from assigned key'
|
|
145
|
-
);
|
|
114
|
+
throw new Error('Invalid writer key: either not owned by requester or different from assigned key');
|
|
146
115
|
}
|
|
147
116
|
}
|
|
148
117
|
}
|
|
@@ -157,18 +126,15 @@ class PartialRoleAccessValidator extends PartialOperationValidator {
|
|
|
157
126
|
}
|
|
158
127
|
|
|
159
128
|
if (!requesterEntry) {
|
|
160
|
-
throw new
|
|
129
|
+
throw new Error('Requester address not found in state');
|
|
161
130
|
}
|
|
162
131
|
const requesterBalance = bufferToBigInt(requesterEntry.balance);
|
|
163
132
|
|
|
164
133
|
const requiredBalance = this.fee * 11n;
|
|
165
134
|
if (requesterBalance < requiredBalance) {
|
|
166
|
-
throw new
|
|
167
|
-
ResultCode.ROLE_INSUFFICIENT_FEE_BALANCE,
|
|
168
|
-
'Insufficient requester balance to cover role access operation FEE.'
|
|
169
|
-
);
|
|
135
|
+
throw new Error('Insufficient requester balance to cover role access operation FEE.');
|
|
170
136
|
}
|
|
171
137
|
}
|
|
172
138
|
}
|
|
173
139
|
|
|
174
|
-
export default
|
|
140
|
+
export default PartialRoleAccess;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
2
|
import {safeDecodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
|
|
3
3
|
import deploymentEntryUtils from "../../../../state/utils/deploymentEntry.js";
|
|
4
|
-
import
|
|
5
|
-
import {ResultCode} from "../../../../../utils/constants.js";
|
|
6
|
-
import SharedValidatorRejectionError from '../errors/SharedValidatorRejectionError.js';
|
|
4
|
+
import PartialOperation from './base/PartialOperation.js';
|
|
7
5
|
|
|
8
|
-
class
|
|
6
|
+
class PartialTransaction extends PartialOperation {
|
|
9
7
|
#config
|
|
10
8
|
|
|
11
9
|
constructor(state, selfAddress, config) {
|
|
@@ -34,20 +32,14 @@ class PartialTransactionValidator extends PartialOperationValidator {
|
|
|
34
32
|
|
|
35
33
|
validateMsbBootstrap(payload) {
|
|
36
34
|
if (!b4a.equals(this.#config.bootstrap, payload.txo.mbs)) {
|
|
37
|
-
throw new
|
|
38
|
-
ResultCode.MSB_BOOTSTRAP_MISMATCH,
|
|
39
|
-
`Declared MSB bootstrap is different than network bootstrap in transaction operation: ${payload.txo.mbs.toString('hex')}`
|
|
40
|
-
);
|
|
35
|
+
throw new Error(`Declared MSB bootstrap is different than network bootstrap in transaction operation: ${payload.txo.mbs.toString('hex')}`);
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
async validateIfExternalBootstrapHasBeenDeployed(payload) {
|
|
45
40
|
const externalBootstrapResult = await this.state.getRegisteredBootstrapEntry(payload.txo.bs.toString('hex'));
|
|
46
41
|
if (externalBootstrapResult === null) {
|
|
47
|
-
throw new
|
|
48
|
-
ResultCode.EXTERNAL_BOOTSTRAP_NOT_DEPLOYED,
|
|
49
|
-
`External bootstrap with hash ${payload.txo.bs.toString('hex')} is not registered as deployment entry.`
|
|
50
|
-
);
|
|
42
|
+
throw new Error(`External bootstrap with hash ${payload.txo.bs.toString('hex')} is not registered as deployment entry.`);
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
const decodedPayload = deploymentEntryUtils.decode(externalBootstrapResult, this.#config.addressLength);
|
|
@@ -55,23 +47,17 @@ class PartialTransactionValidator extends PartialOperationValidator {
|
|
|
55
47
|
const getBootstrapTransactionTxPayload = await this.state.get(txHash.toString('hex'));
|
|
56
48
|
|
|
57
49
|
if (getBootstrapTransactionTxPayload === null) {
|
|
58
|
-
throw new
|
|
59
|
-
ResultCode.EXTERNAL_BOOTSTRAP_TX_MISSING,
|
|
60
|
-
`External bootstrap is not registered as usual tx ${externalBootstrapResult.toString('hex')}: ${payload}`
|
|
61
|
-
);
|
|
50
|
+
throw new Error(`External bootstrap is not registered as usual tx ${externalBootstrapResult.toString('hex')}: ${payload}`);
|
|
62
51
|
}
|
|
63
52
|
|
|
64
53
|
const decodedBootstrapDeployment = safeDecodeApplyOperation(getBootstrapTransactionTxPayload)
|
|
65
54
|
|
|
66
55
|
// edge case
|
|
67
56
|
if (!b4a.equals(decodedBootstrapDeployment.bdo.bs, payload.txo.bs)) {
|
|
68
|
-
throw new
|
|
69
|
-
ResultCode.EXTERNAL_BOOTSTRAP_MISMATCH,
|
|
70
|
-
`External bootstrap does not match the one in the transaction payload: ${decodedBootstrapDeployment.bdo.bs.toString('hex')} !== ${payload.txo.bs.toString('hex')}`
|
|
71
|
-
);
|
|
57
|
+
throw new Error(`External bootstrap does not match the one in the transaction payload: ${decodedBootstrapDeployment.bdo.bs.toString('hex')} !== ${payload.txo.bs.toString('hex')}`);
|
|
72
58
|
}
|
|
73
59
|
}
|
|
74
60
|
|
|
75
61
|
}
|
|
76
62
|
|
|
77
|
-
export default
|
|
63
|
+
export default PartialTransaction;
|
|
@@ -2,11 +2,9 @@ import PeerWallet from 'trac-wallet';
|
|
|
2
2
|
|
|
3
3
|
import {bufferToAddress} from "../../../../state/utils/address.js";
|
|
4
4
|
import {bufferToBigInt} from "../../../../../utils/amountSerialization.js";
|
|
5
|
-
import
|
|
6
|
-
import PartialOperationValidator from './PartialOperationValidator.js';
|
|
7
|
-
import SharedValidatorRejectionError from '../errors/SharedValidatorRejectionError.js';
|
|
5
|
+
import PartialOperation from './base/PartialOperation.js';
|
|
8
6
|
|
|
9
|
-
class
|
|
7
|
+
class PartialTransfer extends PartialOperation {
|
|
10
8
|
#config
|
|
11
9
|
|
|
12
10
|
constructor(state, selfAddress, config) {
|
|
@@ -33,18 +31,12 @@ class PartialTransferValidator extends PartialOperationValidator {
|
|
|
33
31
|
#validateRecipientAddress(payload) {
|
|
34
32
|
const incomingAddress = bufferToAddress(payload.tro.to, this.#config.addressPrefix);
|
|
35
33
|
if (!incomingAddress) {
|
|
36
|
-
throw new
|
|
37
|
-
ResultCode.TRANSFER_RECIPIENT_ADDRESS_INVALID,
|
|
38
|
-
'Invalid recipient address in transfer payload.'
|
|
39
|
-
);
|
|
34
|
+
throw new Error('Invalid recipient address in transfer payload.');
|
|
40
35
|
}
|
|
41
36
|
|
|
42
37
|
const incomingPublicKey = PeerWallet.decodeBech32mSafe(incomingAddress);
|
|
43
38
|
if (incomingPublicKey === null) {
|
|
44
|
-
throw new
|
|
45
|
-
ResultCode.TRANSFER_RECIPIENT_PUBLIC_KEY_INVALID,
|
|
46
|
-
'Invalid recipient public key in transfer payload.'
|
|
47
|
-
);
|
|
39
|
+
throw new Error('Invalid recipient public key in transfer payload.');
|
|
48
40
|
}
|
|
49
41
|
|
|
50
42
|
}
|
|
@@ -55,10 +47,7 @@ class PartialTransferValidator extends PartialOperationValidator {
|
|
|
55
47
|
|
|
56
48
|
const transferAmount = bufferToBigInt(payload.tro.am);
|
|
57
49
|
if (transferAmount > this.max_amount) {
|
|
58
|
-
throw new
|
|
59
|
-
ResultCode.TRANSFER_AMOUNT_TOO_LARGE,
|
|
60
|
-
'Transfer amount exceeds maximum allowed value'
|
|
61
|
-
);
|
|
50
|
+
throw new Error('Transfer amount exceeds maximum allowed value');
|
|
62
51
|
}
|
|
63
52
|
|
|
64
53
|
const isSelfTransfer = senderAddress === recipientAddress;
|
|
@@ -66,15 +55,12 @@ class PartialTransferValidator extends PartialOperationValidator {
|
|
|
66
55
|
|
|
67
56
|
const senderEntry = await this.state.getNodeEntryUnsigned(senderAddress);
|
|
68
57
|
if (!senderEntry) {
|
|
69
|
-
throw new
|
|
58
|
+
throw new Error('Sender account not found');
|
|
70
59
|
}
|
|
71
60
|
|
|
72
61
|
const senderBalance = bufferToBigInt(senderEntry.balance);
|
|
73
62
|
if (!(senderBalance >= totalDeductedAmount)) {
|
|
74
|
-
throw new
|
|
75
|
-
ResultCode.TRANSFER_INSUFFICIENT_BALANCE,
|
|
76
|
-
'Insufficient balance for transfer' + (isSelfTransfer ? ' fee' : ' + fee')
|
|
77
|
-
);
|
|
63
|
+
throw new Error('Insufficient balance for transfer' + (isSelfTransfer ? ' fee' : ' + fee'));
|
|
78
64
|
}
|
|
79
65
|
|
|
80
66
|
if (!isSelfTransfer) {
|
|
@@ -83,14 +69,11 @@ class PartialTransferValidator extends PartialOperationValidator {
|
|
|
83
69
|
const recipientBalance = bufferToBigInt(recipientEntry.balance);
|
|
84
70
|
const newRecipientBalance = recipientBalance + transferAmount;
|
|
85
71
|
if (newRecipientBalance > this.max_amount) {
|
|
86
|
-
throw new
|
|
87
|
-
ResultCode.TRANSFER_RECIPIENT_BALANCE_OVERFLOW,
|
|
88
|
-
'Transfer would cause recipient balance to exceed maximum allowed value'
|
|
89
|
-
);
|
|
72
|
+
throw new Error('Transfer would cause recipient balance to exceed maximum allowed value');
|
|
90
73
|
}
|
|
91
74
|
}
|
|
92
75
|
}
|
|
93
76
|
}
|
|
94
77
|
}
|
|
95
78
|
|
|
96
|
-
export default
|
|
79
|
+
export default PartialTransfer;
|
|
@@ -1,19 +1,18 @@
|
|
|
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
|
|
7
|
-
import {bufferToBigInt} from "
|
|
8
|
-
import {FEE} from "
|
|
9
|
-
import * as operationsUtils from '
|
|
10
|
-
import SharedValidatorRejectionError from '../errors/SharedValidatorRejectionError.js';
|
|
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} 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';
|
|
11
10
|
|
|
12
11
|
const MAX_AMOUNT = BigInt('0xffffffffffffffffffffffffffffffff');
|
|
13
12
|
const FEE_BIGINT = bufferToBigInt(FEE);
|
|
14
13
|
const PUBLIC_KEY_LENGTH = 32;
|
|
15
14
|
|
|
16
|
-
class
|
|
15
|
+
class PartialOperation {
|
|
17
16
|
#state;
|
|
18
17
|
#check;
|
|
19
18
|
#config
|
|
@@ -37,18 +36,18 @@ class PartialOperationValidator {
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
async validate(payload) {
|
|
40
|
-
throw new
|
|
39
|
+
throw new Error("Method 'validate()' must be implemented.");
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
isPayloadSchemaValid(payload) {
|
|
44
43
|
if (!payload || !payload.type) {
|
|
45
|
-
throw new
|
|
44
|
+
throw new Error('Payload or payload type is missing.');
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
const selectedValidator = this.#selectCheckSchemaValidator(payload.type);
|
|
49
48
|
const isPayloadValid = selectedValidator(payload);
|
|
50
49
|
if (!isPayloadValid) {
|
|
51
|
-
throw new
|
|
50
|
+
throw new Error(`Payload is invalid.`);
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
|
|
@@ -65,24 +64,21 @@ class PartialOperationValidator {
|
|
|
65
64
|
case OperationType.TRANSFER:
|
|
66
65
|
return this.check.validateTransferOperation.bind(this.check);
|
|
67
66
|
default:
|
|
68
|
-
throw new
|
|
69
|
-
ResultCode.OPERATION_TYPE_UNKNOWN,
|
|
70
|
-
`Unknown operation type: ${type}`
|
|
71
|
-
);
|
|
67
|
+
throw new Error(`Unknown operation type: ${type}`);
|
|
72
68
|
}
|
|
73
69
|
}
|
|
74
70
|
|
|
75
71
|
validateRequesterAddress(payload) {
|
|
76
72
|
const incomingAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
77
73
|
if (!incomingAddress) {
|
|
78
|
-
throw new
|
|
74
|
+
throw new Error('Invalid requesting address in payload.');
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
const incomingPublicKey = PeerWallet.decodeBech32mSafe(incomingAddress);
|
|
82
78
|
|
|
83
79
|
// 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.
|
|
84
80
|
if (!incomingPublicKey || incomingPublicKey.length !== PUBLIC_KEY_LENGTH) {
|
|
85
|
-
throw new
|
|
81
|
+
throw new Error('Invalid requesting public key in payload.');
|
|
86
82
|
}
|
|
87
83
|
}
|
|
88
84
|
|
|
@@ -131,10 +127,7 @@ class PartialOperationValidator {
|
|
|
131
127
|
OperationType.TRANSFER
|
|
132
128
|
];
|
|
133
129
|
default:
|
|
134
|
-
throw new
|
|
135
|
-
ResultCode.OPERATION_TYPE_UNKNOWN,
|
|
136
|
-
`Unknown operation type: ${payload.type}`
|
|
137
|
-
);
|
|
130
|
+
throw new Error(`Unknown operation type: ${payload.type}`);
|
|
138
131
|
}
|
|
139
132
|
}
|
|
140
133
|
|
|
@@ -150,14 +143,11 @@ class PartialOperationValidator {
|
|
|
150
143
|
const messageHash = await PeerWallet.blake3(message);
|
|
151
144
|
const payloadHash = operation.tx;
|
|
152
145
|
if (!b4a.equals(payloadHash, messageHash)) {
|
|
153
|
-
throw new
|
|
154
|
-
ResultCode.TX_HASH_MISMATCH,
|
|
155
|
-
'Regenerated transaction does not match incoming transaction in payload.'
|
|
156
|
-
);
|
|
146
|
+
throw new Error('Regenerated transaction does not match incoming transaction in payload.');
|
|
157
147
|
}
|
|
158
148
|
|
|
159
149
|
if (!PeerWallet.verify(incomingSignature, messageHash, incomingPublicKey)) {
|
|
160
|
-
throw new
|
|
150
|
+
throw new Error('Invalid signature in payload.');
|
|
161
151
|
}
|
|
162
152
|
}
|
|
163
153
|
|
|
@@ -168,7 +158,7 @@ class PartialOperationValidator {
|
|
|
168
158
|
const incomingTxv = operation.txv
|
|
169
159
|
|
|
170
160
|
if (!b4a.equals(currentTxv, incomingTxv)) {
|
|
171
|
-
throw new
|
|
161
|
+
throw new Error(`Transaction has expired.`);
|
|
172
162
|
}
|
|
173
163
|
}
|
|
174
164
|
|
|
@@ -179,10 +169,7 @@ class PartialOperationValidator {
|
|
|
179
169
|
const txHex = tx.toString('hex');
|
|
180
170
|
|
|
181
171
|
if (await this.state.get(txHex) !== null) {
|
|
182
|
-
throw new
|
|
183
|
-
ResultCode.TX_ALREADY_EXISTS,
|
|
184
|
-
`Transaction with hash ${txHex} already exists in the state.`
|
|
185
|
-
);
|
|
172
|
+
throw new Error(`Transaction with hash ${txHex} already exists in the state.`);
|
|
186
173
|
}
|
|
187
174
|
}
|
|
188
175
|
|
|
@@ -193,10 +180,7 @@ class PartialOperationValidator {
|
|
|
193
180
|
|
|
194
181
|
const condition = va === undefined && vn === undefined && vs === undefined
|
|
195
182
|
if (!condition) {
|
|
196
|
-
throw new
|
|
197
|
-
ResultCode.OPERATION_ALREADY_COMPLETED,
|
|
198
|
-
'Transfer operation must not be completed already (va, vn, vs must be undefined).'
|
|
199
|
-
);
|
|
183
|
+
throw new Error('Transfer operation must not be completed already (va, vn, vs must be undefined).');
|
|
200
184
|
}
|
|
201
185
|
}
|
|
202
186
|
|
|
@@ -210,12 +194,12 @@ class PartialOperationValidator {
|
|
|
210
194
|
}
|
|
211
195
|
|
|
212
196
|
if (!requesterEntry) {
|
|
213
|
-
throw new
|
|
197
|
+
throw new Error('Requester address not found in state');
|
|
214
198
|
}
|
|
215
199
|
|
|
216
200
|
const requesterBalance = bufferToBigInt(requesterEntry.balance);
|
|
217
201
|
if (requesterBalance < FEE_BIGINT) {
|
|
218
|
-
throw new
|
|
202
|
+
throw new Error('Insufficient balance to cover transaction fee.');
|
|
219
203
|
}
|
|
220
204
|
}
|
|
221
205
|
|
|
@@ -225,10 +209,7 @@ class PartialOperationValidator {
|
|
|
225
209
|
const operation = payload[operationKey];
|
|
226
210
|
const bs = operation.bs;
|
|
227
211
|
if (b4a.equals(this.#config.bootstrap, bs)) {
|
|
228
|
-
throw new
|
|
229
|
-
ResultCode.EXTERNAL_BOOTSTRAP_EQUALS_MSB_BOOTSTRAP,
|
|
230
|
-
`External bootstrap is the same as MSB bootstrap: ${bs.toString('hex')}`
|
|
231
|
-
);
|
|
212
|
+
throw new Error(`External bootstrap is the same as MSB bootstrap: ${bs.toString('hex')}`);
|
|
232
213
|
}
|
|
233
214
|
}
|
|
234
215
|
|
|
@@ -242,13 +223,10 @@ class PartialOperationValidator {
|
|
|
242
223
|
|
|
243
224
|
const requesterAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
244
225
|
if (this.#selfAddress === requesterAddress) {
|
|
245
|
-
throw new
|
|
246
|
-
ResultCode.SELF_VALIDATION_FORBIDDEN,
|
|
247
|
-
'Requester address cannot be the same as the validator wallet address.'
|
|
248
|
-
);
|
|
226
|
+
throw new Error('Requester address cannot be the same as the validator wallet address.');
|
|
249
227
|
}
|
|
250
228
|
}
|
|
251
229
|
|
|
252
230
|
}
|
|
253
231
|
|
|
254
|
-
export default
|
|
232
|
+
export default PartialOperation;
|
|
@@ -1,99 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import b4a from 'b4a'
|
|
3
|
-
import { NetworkOperationType, V1_PROTOCOL_PAYLOAD_MAX_SIZE } from '../../../../utils/constants.js'
|
|
4
|
-
import { publicKeyToAddress } from '../../../../utils/helpers.js'
|
|
5
|
-
import V1LivenessOperationHandler from './handlers/V1LivenessOperationHandler.js'
|
|
6
|
-
import V1BroadcastTransactionOperationHandler from './handlers/V1BroadcastTransactionOperationHandler.js'
|
|
1
|
+
import { MessageHeader } from '../../../../utils/protobuf/network.cjs';
|
|
7
2
|
|
|
8
3
|
class NetworkMessageRouterV1 {
|
|
9
|
-
#config
|
|
10
|
-
#livenessRequestHandler
|
|
11
|
-
#broadcastTransactionHandler
|
|
4
|
+
#config;
|
|
12
5
|
|
|
13
|
-
constructor(
|
|
14
|
-
|
|
15
|
-
wallet,
|
|
16
|
-
rateLimiterService,
|
|
17
|
-
txPoolService,
|
|
18
|
-
pendingRequestsService,
|
|
19
|
-
transactionCommitService,
|
|
20
|
-
config
|
|
21
|
-
) {
|
|
22
|
-
this.#config = config
|
|
23
|
-
this.#livenessRequestHandler = new V1LivenessOperationHandler(
|
|
24
|
-
wallet,
|
|
25
|
-
rateLimiterService,
|
|
26
|
-
pendingRequestsService,
|
|
27
|
-
config
|
|
28
|
-
);
|
|
29
|
-
this.#broadcastTransactionHandler = new V1BroadcastTransactionOperationHandler(
|
|
30
|
-
state,
|
|
31
|
-
wallet,
|
|
32
|
-
rateLimiterService,
|
|
33
|
-
txPoolService,
|
|
34
|
-
pendingRequestsService,
|
|
35
|
-
transactionCommitService,
|
|
36
|
-
config
|
|
37
|
-
);
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.#config = config;
|
|
38
8
|
}
|
|
39
9
|
|
|
40
|
-
async route(incomingMessage
|
|
41
|
-
|
|
42
|
-
this.#disconnect(connection, 'Pre-validation failed for incoming V1 message')
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
let decodedMessage;
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
decodedMessage = decodeV1networkOperation(incomingMessage)
|
|
49
|
-
} catch (error) {
|
|
50
|
-
this.#disconnect(connection, `Failed to decode incoming V1 message: ${error.message}`)
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// TODO: Decide if we really need to check decodedMessage.type here, since this is done
|
|
55
|
-
// again in the next switch statement
|
|
56
|
-
if (!decodedMessage || !Number.isInteger(decodedMessage.type) || decodedMessage.type <= 0) {
|
|
57
|
-
this.#disconnect(connection, `Invalid V1 message type: ${decodedMessage?.type}`)
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// We received a v1 message, so we set the connection protocol accordingly
|
|
62
|
-
connection.protocolSession.setV1AsPreferredProtocol()
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
switch (decodedMessage.type) {
|
|
66
|
-
case NetworkOperationType.LIVENESS_REQUEST:
|
|
67
|
-
await this.#livenessRequestHandler.handleRequest(decodedMessage, connection);
|
|
68
|
-
break;
|
|
69
|
-
case NetworkOperationType.LIVENESS_RESPONSE:
|
|
70
|
-
await this.#livenessRequestHandler.handleResponse(decodedMessage, connection);
|
|
71
|
-
break;
|
|
72
|
-
|
|
73
|
-
case NetworkOperationType.BROADCAST_TRANSACTION_REQUEST:
|
|
74
|
-
await this.#broadcastTransactionHandler.handleRequest(decodedMessage, connection);
|
|
75
|
-
break;
|
|
76
|
-
|
|
77
|
-
case NetworkOperationType.BROADCAST_TRANSACTION_RESPONSE:
|
|
78
|
-
await this.#broadcastTransactionHandler.handleResponse(decodedMessage, connection);
|
|
79
|
-
break;
|
|
80
|
-
default:
|
|
81
|
-
this.#disconnect(connection, `Unsupported V1 message type: ${decodedMessage.type}`)
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
this.#disconnect(connection, `Unhandled error while routing V1 message: ${error.message}`)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
#preValidate(incomingMessage) {
|
|
89
|
-
return !(!incomingMessage || !b4a.isBuffer(incomingMessage) || incomingMessage.length === 0 || incomingMessage.length > V1_PROTOCOL_PAYLOAD_MAX_SIZE);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
#disconnect(connection, reason) {
|
|
93
|
-
const sender = publicKeyToAddress(connection.remotePublicKey, this.#config)
|
|
94
|
-
console.error(`NetworkMessageRouterV1: ${reason}, sender: ${sender}`)
|
|
95
|
-
connection.end();
|
|
10
|
+
async route(incomingMessage) {
|
|
11
|
+
MessageHeader.decode(incomingMessage);
|
|
96
12
|
}
|
|
97
13
|
}
|
|
98
14
|
|
|
99
|
-
export default NetworkMessageRouterV1
|
|
15
|
+
export default NetworkMessageRouterV1;
|