trac-msb 0.2.26 → 0.3.0-beta.1

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.
Files changed (136) hide show
  1. package/SECURITY.md +2 -2
  2. package/cli/commandHandler.js +317 -20
  3. package/cli/handlers.js +9 -1
  4. package/cli/interactive.js +4 -3
  5. package/cli/tokenSupplyAudit.js +90 -0
  6. package/msb.mjs +1 -0
  7. package/package.json +15 -6
  8. package/rpc/create_server.js +12 -3
  9. package/rpc/handlers.js +103 -0
  10. package/rpc/routes/v1.js +8 -0
  11. package/src/codecs/apply/applyOperationCodec.js +334 -0
  12. package/src/codecs/apply/applyOperations.generated.cjs +6380 -0
  13. package/src/codecs/consensus/v1/consensusV1.generated.cjs +3508 -0
  14. package/src/codecs/consensus/v1/consensusV1OperationCodec.js +161 -0
  15. package/src/codecs/consensus/v1/vdfConfigCodec.js +83 -0
  16. package/src/{utils/protobuf → codecs/network/v1}/networkV1.generated.cjs +664 -393
  17. package/src/codecs/network/v1/networkV1OperationCodec.js +25 -0
  18. package/src/codecs/protobufRuntime.cjs +95 -0
  19. package/src/codecs/state/escrowEntryCodec.js +45 -0
  20. package/src/codecs/state/state.generated.cjs +659 -0
  21. package/src/config/args.js +0 -1
  22. package/src/config/config.js +41 -7
  23. package/src/config/env.js +33 -9
  24. package/src/core/consensus/epochAppenderSelection/EpochAppenderSelectionError.js +55 -0
  25. package/src/core/consensus/epochAppenderSelection/EpochAppenderSelectionOperations.js +118 -0
  26. package/src/core/consensus/epochAppenderSelection/EpochAppenderSelector.js +1042 -0
  27. package/src/core/consensus/epochAppenderSelection/createEpochAppenderSelector.js +23 -0
  28. package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionLedgerReader.js +274 -0
  29. package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionState.js +488 -0
  30. package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionStorage.js +93 -0
  31. package/src/core/consensus/quorum.js +11 -0
  32. package/src/core/consensus/services/EpochCoordinationRound.js +839 -0
  33. package/src/core/consensus/services/EpochCoordinatorOperations.js +211 -0
  34. package/src/core/consensus/services/EpochCoordinatorService.js +335 -0
  35. package/src/core/consensus/services/EpochRoundListeners.js +116 -0
  36. package/src/core/consensus/services/EpochStateMachine.js +141 -0
  37. package/src/core/consensus/services/VDFBare.js +291 -0
  38. package/src/core/consensus/services/VDFNode.js +111 -0
  39. package/src/core/consensus/services/VDFProgressWatchdog.js +79 -0
  40. package/src/core/consensus/services/VDFService.js +87 -0
  41. package/src/core/consensus/services/VDFServiceManager.js +147 -0
  42. package/src/core/consensus/services/createVDFService.js +16 -0
  43. package/src/core/consensus/services/vdf-guardian.js +32 -0
  44. package/src/core/consensus/services/vdf-worker.js +90 -0
  45. package/src/core/network/Network.js +327 -238
  46. package/src/core/network/protocols/consensus/IndexerMessages.js +72 -0
  47. package/src/core/network/protocols/consensus/v1/ConsensusV1ConnectionPolicy.js +57 -0
  48. package/src/core/network/protocols/consensus/v1/ConsensusV1MessageRouter.js +147 -0
  49. package/src/core/network/protocols/consensus/v1/ConsensusV1Protocol.js +72 -0
  50. package/src/core/network/protocols/consensus/v1/IndexerPendingRequestService.js +108 -0
  51. package/src/core/network/protocols/consensus/v1/V1ConsensusProtocolError.js +29 -0
  52. package/src/core/network/protocols/consensus/v1/handlers/V1EpochAppendOperationHandler.js +68 -0
  53. package/src/core/network/protocols/consensus/v1/handlers/V1EpochProofProposalOperationHandler.js +226 -0
  54. package/src/core/network/protocols/consensus/v1/validators/ConsensusValidationSchema.js +192 -0
  55. package/src/core/network/protocols/consensus/v1/validators/V1BaseConsensusOperation.js +428 -0
  56. package/src/core/network/protocols/consensus/v1/validators/V1EpochAppendReply.js +47 -0
  57. package/src/core/network/protocols/consensus/v1/validators/V1EpochAppendRequest.js +17 -0
  58. package/src/core/network/protocols/consensus/v1/validators/V1EpochProofProposalApproval.js +116 -0
  59. package/src/core/network/protocols/consensus/v1/validators/V1EpochProofProposalRequest.js +152 -0
  60. package/src/core/network/protocols/consensus/v1/validators/consensusValidationSchemaHelpers.js +145 -0
  61. package/src/core/network/protocols/shared/ConnectionOperationHandler.js +27 -0
  62. package/src/core/network/protocols/{NetworkMessages.js → validators/ValidatorMessages.js} +24 -14
  63. package/src/core/network/protocols/{ProtocolSession.js → validators/ValidatorProtocolSession.js} +22 -25
  64. package/src/core/network/protocols/{legacy/NetworkMessageRouter.js → validators/legacy/ValidatorLegacyMessageRouter.js} +6 -6
  65. package/src/core/network/protocols/{LegacyProtocol.js → validators/legacy/ValidatorLegacyProtocol.js} +9 -7
  66. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/BaseStateOperationHandler.js +1 -1
  67. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyGetRequestHandler.js +5 -5
  68. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyRoleOperationHandler.js +5 -5
  69. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacySubnetworkOperationHandler.js +5 -5
  70. package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyTransferOperationHandler.js +5 -5
  71. package/src/core/network/protocols/{ProtocolInterface.js → validators/shared/ValidatorProtocolInterface.js} +5 -5
  72. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialBootstrapDeploymentValidator.js +1 -1
  73. package/src/core/network/protocols/validators/shared/validators/PartialHtlcValidator.js +325 -0
  74. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialOperationValidator.js +44 -21
  75. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialRoleAccessValidator.js +3 -3
  76. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialTransactionValidator.js +3 -3
  77. package/src/core/network/protocols/{shared → validators/shared}/validators/PartialTransferValidator.js +4 -4
  78. package/src/core/network/protocols/{v1 → validators/v1}/V1ProtocolError.js +1 -1
  79. package/src/core/network/{services/PendingRequestService.js → protocols/validators/v1/ValidatorPendingRequestService.js} +19 -82
  80. package/src/core/network/protocols/{v1/NetworkMessageRouter.js → validators/v1/ValidatorV1MessageRouter.js} +9 -9
  81. package/src/core/network/protocols/{V1Protocol.js → validators/v1/ValidatorV1Protocol.js} +10 -8
  82. package/src/core/network/protocols/{connectionPolicies.js → validators/v1/connectionPolicies.js} +1 -1
  83. package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1BaseOperationHandler.js +6 -23
  84. package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1BroadcastTransactionOperationHandler.js +14 -9
  85. package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1LivenessOperationHandler.js +5 -4
  86. package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionErrorMapper.js +3 -3
  87. package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionOperationStrategies.js +44 -1
  88. package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionPayloadUtils.js +1 -1
  89. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BaseOperation.js +19 -15
  90. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BroadcastTransactionRequest.js +2 -2
  91. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BroadcastTransactionResponse.js +34 -18
  92. package/src/core/network/protocols/{v1 → validators/v1}/validators/V1ValidationSchema.js +3 -3
  93. package/src/core/network/services/IndexerConnectionManager.js +88 -0
  94. package/src/core/network/services/TransactionPoolService.js +11 -18
  95. package/src/core/network/services/TransactionRateLimiterService.js +1 -1
  96. package/src/core/network/services/ValidatorConnectionManager.js +163 -0
  97. package/src/core/network/services/{MessageOrchestrator.js → ValidatorMessageOrchestrator.js} +34 -42
  98. package/src/core/network/services/ValidatorObserverService.js +21 -36
  99. package/src/core/network/shared/BasePendingRequestService.js +67 -0
  100. package/src/core/network/shared/PeerConnectionManager.js +185 -0
  101. package/src/core/state/State.js +1789 -173
  102. package/src/core/state/StateEventQueue.js +69 -0
  103. package/src/core/state/utils/address.js +10 -6
  104. package/src/core/state/utils/epochProof.js +109 -0
  105. package/src/core/state/utils/escrowEntry.js +178 -0
  106. package/src/core/state/validators/StateValidationSchema.js +757 -0
  107. package/src/core/state/validators/stateValidationRules.js +312 -0
  108. package/src/index.js +255 -65
  109. package/src/messages/consensus/v1/ConsensusMessageBuilder.js +368 -0
  110. package/src/messages/consensus/v1/ConsensusMessageDirector.js +142 -0
  111. package/src/messages/consensus/v1/consensusMessageFactory.js +12 -0
  112. package/src/messages/consensus/v1/epochAppendMessage.js +58 -0
  113. package/src/messages/network/v1/NetworkMessageBuilder.js +15 -7
  114. package/src/messages/state/ApplyStateMessageBuilder.js +499 -13
  115. package/src/messages/state/ApplyStateMessageDirector.js +261 -2
  116. package/src/utils/StateMachine.js +125 -0
  117. package/src/utils/applyOperations.js +45 -2
  118. package/src/utils/buffer.js +239 -59
  119. package/src/utils/consensusConfig.js +76 -0
  120. package/src/utils/constants.js +174 -73
  121. package/src/utils/helpers.js +0 -25
  122. package/src/utils/htlcLock.js +122 -0
  123. package/src/utils/logger.js +10 -4
  124. package/src/utils/migrationUtils.js +2 -1
  125. package/src/utils/normalizers.js +113 -0
  126. package/src/utils/scheduler/SchedulableService.js +107 -0
  127. package/src/utils/{Scheduler.js → scheduler/Scheduler.js} +56 -17
  128. package/src/core/network/services/ConnectionManager.js +0 -406
  129. package/src/utils/check.js +0 -525
  130. package/src/utils/protobuf/applyOperations.cjs +0 -1373
  131. package/src/utils/protobuf/operationHelpers.js +0 -81
  132. /package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyResponseHandler.js +0 -0
  133. /package/src/core/network/protocols/{legacy → validators/legacy}/validators/ValidatorResponse.js +0 -0
  134. /package/src/core/network/protocols/{legacy → validators/legacy}/validators/base/BaseResponse.js +0 -0
  135. /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessRequest.js +0 -0
  136. /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessResponse.js +0 -0
@@ -1,4 +1,4 @@
1
- import { NETWORK_MESSAGE_TYPES } from '../../../../../utils/constants.js';
1
+ import { NETWORK_MESSAGE_TYPES } from '../../../../../../utils/constants.js';
2
2
  import tracCryptoApi from 'trac-crypto-api';
3
3
  import b4a from 'b4a';
4
4
 
@@ -15,17 +15,17 @@ class LegacyGetRequestHandler {
15
15
  return this.#state;
16
16
  }
17
17
 
18
- async handle(message, connection, channelString) {
18
+ async handle(message, connection, protocolSession, channelString) {
19
19
  switch (message) {
20
20
  case NETWORK_MESSAGE_TYPES.GET.VALIDATOR:
21
- await this.handleGetValidatorResponse(connection, channelString);
21
+ await this.handleGetValidatorResponse(connection, protocolSession, channelString);
22
22
  break;
23
23
  default:
24
24
  throw new Error(`Unhandled GET type: ${message}`);
25
25
  }
26
26
  }
27
27
 
28
- async handleGetValidatorResponse(connection, channelString) {
28
+ async handleGetValidatorResponse(connection, protocolSession, channelString) {
29
29
  const nonce = b4a.toString(tracCryptoApi.nonce.generate(), 'hex');
30
30
  const payload = {
31
31
  op: 'validatorResponse',
@@ -46,7 +46,7 @@ class LegacyGetRequestHandler {
46
46
  ...payload,
47
47
  sig: sig.toString('hex'),
48
48
  };
49
- connection.protocolSession.sendAndForget(responseMessage)
49
+ protocolSession.sendAndForget(responseMessage)
50
50
  }
51
51
  }
52
52
 
@@ -1,10 +1,10 @@
1
- import {OperationType} from '../../../../../utils/constants.js';
1
+ import {OperationType} from '../../../../../../utils/constants.js';
2
2
  import PartialRoleAccessValidator from "../../shared/validators/PartialRoleAccessValidator.js";
3
3
  import BaseStateOperationHandler from './BaseStateOperationHandler.js';
4
- import {applyStateMessageFactory} from "../../../../../messages/state/applyStateMessageFactory.js";
5
- import {safeEncodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
6
- import {normalizeRoleAccessOperation} from "../../../../../utils/normalizers.js";
7
- import { publicKeyToAddress } from "../../../../../utils/helpers.js"
4
+ import {applyStateMessageFactory} from "../../../../../../messages/state/applyStateMessageFactory.js";
5
+ import {safeEncodeApplyOperation} from "../../../../../../codecs/apply/applyOperationCodec.js";
6
+ import {normalizeRoleAccessOperation} from "../../../../../../utils/normalizers.js";
7
+ import { publicKeyToAddress } from "../../../../../../utils/helpers.js"
8
8
  import b4a from "b4a";
9
9
 
10
10
  class LegacyRoleOperationHandler extends BaseStateOperationHandler {
@@ -1,17 +1,17 @@
1
1
  import BaseStateOperationHandler from './BaseStateOperationHandler.js';
2
2
  import {
3
3
  OperationType
4
- } from '../../../../../utils/constants.js';
4
+ } from '../../../../../../utils/constants.js';
5
5
  import PartialBootstrapDeploymentValidator from "../../shared/validators/PartialBootstrapDeploymentValidator.js";
6
6
  import PartialTransactionValidator from "../../shared/validators/PartialTransactionValidator.js";
7
- import {applyStateMessageFactory} from "../../../../../messages/state/applyStateMessageFactory.js";
8
- import {safeEncodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
7
+ import {applyStateMessageFactory} from "../../../../../../messages/state/applyStateMessageFactory.js";
8
+ import {safeEncodeApplyOperation} from "../../../../../../codecs/apply/applyOperationCodec.js";
9
9
  import {
10
10
  normalizeBootstrapDeploymentOperation,
11
11
  normalizeTransactionOperation
12
- } from "../../../../../utils/normalizers.js";
12
+ } from "../../../../../../utils/normalizers.js";
13
13
  import b4a from "b4a";
14
- import {publicKeyToAddress} from "../../../../../utils/helpers.js";
14
+ import {publicKeyToAddress} from "../../../../../../utils/helpers.js";
15
15
 
16
16
 
17
17
  class LegacySubnetworkOperationHandler extends BaseStateOperationHandler {
@@ -1,11 +1,11 @@
1
1
  import BaseStateOperationHandler from './BaseStateOperationHandler.js';
2
- import {OperationType} from '../../../../../utils/constants.js';
2
+ import {OperationType} from '../../../../../../utils/constants.js';
3
3
  import PartialTransferValidator from "../../shared/validators/PartialTransferValidator.js";
4
- import {normalizeTransferOperation} from "../../../../../utils/normalizers.js"
5
- import {applyStateMessageFactory} from "../../../../../messages/state/applyStateMessageFactory.js";
6
- import {safeEncodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
4
+ import {normalizeTransferOperation} from "../../../../../../utils/normalizers.js"
5
+ import {applyStateMessageFactory} from "../../../../../../messages/state/applyStateMessageFactory.js";
6
+ import {safeEncodeApplyOperation} from "../../../../../../codecs/apply/applyOperationCodec.js";
7
7
  import b4a from "b4a";
8
- import {publicKeyToAddress} from "../../../../../utils/helpers.js";
8
+ import {publicKeyToAddress} from "../../../../../../utils/helpers.js";
9
9
 
10
10
  class LegacyTransferOperationHandler extends BaseStateOperationHandler {
11
11
  #partialTransferValidator;
@@ -1,14 +1,14 @@
1
1
  /**
2
- * ProtocolInterface serves as a base class for all network protocol implementations.
2
+ * ValidatorProtocolInterface serves as a base class for validator protocol implementations.
3
3
  * @interface
4
4
  */
5
5
 
6
- class ProtocolInterface {
6
+ class ValidatorProtocolInterface {
7
7
 
8
8
  // TODO: Refactor this so we don't need to pass a reference for the whole network instance
9
9
  constructor(_router, _connection, _pendingRequestService, _config) {
10
- if (new.target === ProtocolInterface) {
11
- throw new Error('ProtocolInterface cannot be instantiated directly');
10
+ if (new.target === ValidatorProtocolInterface) {
11
+ throw new Error('ValidatorProtocolInterface cannot be instantiated directly');
12
12
  }
13
13
  }
14
14
 
@@ -40,4 +40,4 @@ class ProtocolInterface {
40
40
  }
41
41
  }
42
42
 
43
- export default ProtocolInterface;
43
+ export default ValidatorProtocolInterface;
@@ -1,5 +1,5 @@
1
1
  import PartialOperationValidator from './PartialOperationValidator.js';
2
- import {ResultCode} from "../../../../../utils/constants.js";
2
+ import {ResultCode} from "../../../../../../utils/constants.js";
3
3
  import {V1ProtocolError} from '../../v1/V1ProtocolError.js';
4
4
 
5
5
  class PartialBootstrapDeploymentValidator extends PartialOperationValidator {
@@ -0,0 +1,325 @@
1
+ import b4a from 'b4a';
2
+ import tracCryptoApi from 'trac-crypto-api';
3
+
4
+ import PartialOperationValidator from './PartialOperationValidator.js';
5
+ import {bufferToAddress} from '../../../../../state/utils/address.js';
6
+ import {BALANCE_FEE, PERCENT_75, toBalance} from '../../../../../state/utils/balance.js';
7
+ import * as escrowEntryUtils from '../../../../../state/utils/escrowEntry.js';
8
+ import nodeEntryUtils from '../../../../../state/utils/nodeEntry.js';
9
+ import {bufferToBigInt} from '../../../../../../utils/amountSerialization.js';
10
+ import {isZeroBuffer, uint64ToBuffer} from '../../../../../../utils/buffer.js';
11
+ import {
12
+ HTLC_MIN_LOCK_DURATION_EPOCHS,
13
+ EntryType,
14
+ OperationType,
15
+ PUBLIC_KEY_LENGTH,
16
+ ResultCode
17
+ } from '../../../../../../utils/constants.js';
18
+ import {verifyOrderedHtlcCosignerSignatures} from '../../../../../../utils/htlcLock.js';
19
+ import {V1ProtocolError} from '../../v1/V1ProtocolError.js';
20
+
21
+ class PartialHtlcValidator extends PartialOperationValidator {
22
+ #config;
23
+ #selfAddress;
24
+
25
+ constructor(state, selfAddress, config) {
26
+ super(state, selfAddress, config);
27
+ this.#config = config;
28
+ this.#selfAddress = selfAddress;
29
+ }
30
+
31
+ async validate(payload) {
32
+ if (payload?.type === OperationType.HTLC_CLAIM) {
33
+ return this.#validateClaim(payload);
34
+ }
35
+ if (payload?.type === OperationType.HTLC_REFUND) return this.#validateRefund(payload);
36
+ if (payload?.type !== OperationType.HTLC_LOCK) {
37
+ throw new V1ProtocolError(ResultCode.OPERATION_TYPE_UNKNOWN, 'Unsupported HTLC operation type.');
38
+ }
39
+ this.isPayloadSchemaValid(payload);
40
+ this.validateNoSelfValidation(payload);
41
+ this.validateRequesterAddress(payload);
42
+ await this.#validateLockUniqueness(payload.hlo.tx);
43
+ await this.validateSignature(payload);
44
+ await this.validateTransactionValidity(payload);
45
+ this.isOperationNotCompleted(payload);
46
+
47
+ this.#validateSettlementAddresses(payload.hlo);
48
+ this.#validateLockAuthorizations(payload);
49
+ await this.#validateRefundEpoch(payload.hlo);
50
+ await this.#validateLockerBalance(payload);
51
+ await this.#validateValidatorRewardBalance();
52
+
53
+ return true;
54
+ }
55
+
56
+ async #validateClaim(payload) {
57
+ this.isPayloadSchemaValid(payload);
58
+ this.validateNoSelfValidation(payload);
59
+ this.validateRequesterAddress(payload);
60
+ await this.validateTransactionUniqueness(payload);
61
+ await this.validateSignature(payload);
62
+ await this.validateTransactionValidity(payload);
63
+ this.isOperationNotCompleted(payload);
64
+
65
+ const escrowKey = EntryType.HTLC_ESCROW + b4a.toString(payload.hco.li, 'hex');
66
+ const signedEscrowBuffer = await this.state.getSigned(escrowKey);
67
+ const signedEscrow = escrowEntryUtils.decode(signedEscrowBuffer, this.#config.addressPrefix);
68
+ if (!signedEscrow || !b4a.equals(signedEscrow.lockId, payload.hco.li)) {
69
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC lock is not confirmed in signed state.');
70
+ }
71
+ if (signedEscrow.status !== escrowEntryUtils.Status.PENDING) {
72
+ throw new V1ProtocolError(ResultCode.TX_ALREADY_EXISTS, 'HTLC escrow is already settled.');
73
+ }
74
+
75
+ const escrowBuffer = await this.state.get(escrowKey);
76
+ const escrow = escrowEntryUtils.decode(escrowBuffer, this.#config.addressPrefix);
77
+ if (!escrow || !b4a.equals(escrow.lockId, payload.hco.li)) {
78
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC escrow does not exist or is invalid.');
79
+ }
80
+ if (escrow.status !== escrowEntryUtils.Status.PENDING) {
81
+ throw new V1ProtocolError(ResultCode.TX_ALREADY_EXISTS, 'HTLC escrow is already settled.');
82
+ }
83
+
84
+ const [signedEpoch, unsignedEpoch] = await Promise.all([
85
+ this.state.getCurrentEpoch(),
86
+ this.state.getCurrentEpochUnsigned()
87
+ ]);
88
+ if (typeof signedEpoch !== 'bigint' || typeof unsignedEpoch !== 'bigint') {
89
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC claim requires an initialized network epoch.');
90
+ }
91
+ const currentEpoch = signedEpoch > unsignedEpoch ? signedEpoch : unsignedEpoch;
92
+ if (currentEpoch >= escrow.refundEpoch.readBigUInt64BE(0)) {
93
+ throw new V1ProtocolError(ResultCode.TX_EXPIRED, 'HTLC claim window has expired.');
94
+ }
95
+
96
+ const settlement = escrowEntryUtils.makeClaim(
97
+ escrowBuffer,
98
+ payload.hco.pi,
99
+ uint64ToBuffer(currentEpoch),
100
+ this.#config.addressPrefix
101
+ );
102
+ if (!settlement) {
103
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC claim preimage is invalid.');
104
+ }
105
+ await this.#validateClaimRecipientBalances(settlement);
106
+ return true;
107
+ }
108
+
109
+ async #validateClaimRecipientBalances(settlement) {
110
+ const credits = new Map();
111
+ for (const [addressBytes, amount] of [
112
+ [settlement.address, settlement.amount],
113
+ [settlement.additionalFeeRecipientAddress, settlement.additionalFeeAmount]
114
+ ]) {
115
+ if (addressBytes === null) continue;
116
+ const address = bufferToAddress(addressBytes, this.#config.addressPrefix);
117
+ const credit = amount?.asBigInt();
118
+ if (address === null || typeof credit !== 'bigint') {
119
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC settlement recipient or amount is invalid.');
120
+ }
121
+ credits.set(address, (credits.get(address) ?? 0n) + credit);
122
+ }
123
+
124
+ for (const [address, credit] of credits) {
125
+ const recipient = await this.state.getNodeEntryUnsigned(address);
126
+ if (!recipient) continue;
127
+ const balance = toBalance(recipient.balance);
128
+ if (!balance || balance.asBigInt() + credit > this.max_amount) {
129
+ throw new V1ProtocolError(
130
+ ResultCode.TRANSFER_RECIPIENT_BALANCE_OVERFLOW,
131
+ 'HTLC claim would overflow a settlement recipient balance.'
132
+ );
133
+ }
134
+ }
135
+ }
136
+
137
+ async #validateRefund(payload) {
138
+ this.isPayloadSchemaValid(payload);
139
+ this.validateNoSelfValidation(payload);
140
+ this.validateRequesterAddress(payload);
141
+ await this.validateTransactionUniqueness(payload);
142
+ await this.validateSignature(payload);
143
+ await this.validateTransactionValidity(payload);
144
+ this.isOperationNotCompleted(payload);
145
+
146
+ const escrowKey = EntryType.HTLC_ESCROW + b4a.toString(payload.hro.li, 'hex');
147
+ const signedEscrow = escrowEntryUtils.decode(
148
+ await this.state.getSigned(escrowKey), this.#config.addressPrefix
149
+ );
150
+ if (!signedEscrow || !b4a.equals(signedEscrow.lockId, payload.hro.li)) {
151
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC lock is not confirmed in signed state.');
152
+ }
153
+ if (signedEscrow.status !== escrowEntryUtils.Status.PENDING) {
154
+ throw new V1ProtocolError(ResultCode.TX_ALREADY_EXISTS, 'HTLC escrow is already settled.');
155
+ }
156
+
157
+ const escrowBuffer = await this.state.get(escrowKey);
158
+ const escrow = escrowEntryUtils.decode(escrowBuffer, this.#config.addressPrefix);
159
+ if (!escrow || !b4a.equals(escrow.lockId, payload.hro.li)) {
160
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC escrow is missing from current state.');
161
+ }
162
+ if (escrow.status !== escrowEntryUtils.Status.PENDING) {
163
+ throw new V1ProtocolError(ResultCode.TX_ALREADY_EXISTS, 'HTLC escrow is already settled.');
164
+ }
165
+
166
+ const [signedEpoch, unsignedEpoch] = await Promise.all([
167
+ this.state.getCurrentEpoch(),
168
+ this.state.getCurrentEpochUnsigned()
169
+ ]);
170
+ if (typeof signedEpoch !== 'bigint' || typeof unsignedEpoch !== 'bigint') {
171
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC refunds require an initialized network epoch.');
172
+ }
173
+ const refundEpoch = b4a.from(escrow.refundEpoch).readBigUInt64BE(0);
174
+ if (signedEpoch < refundEpoch || unsignedEpoch < refundEpoch) {
175
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC refund epoch has not been reached.');
176
+ }
177
+
178
+ const settlement = escrowEntryUtils.makeRefund(
179
+ escrowBuffer, uint64ToBuffer(unsignedEpoch), this.#config.addressPrefix
180
+ );
181
+ if (!settlement) {
182
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC refund transition is invalid.');
183
+ }
184
+ const recipientAddress = bufferToAddress(settlement.address, this.#config.addressPrefix);
185
+ if (recipientAddress === null) {
186
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC refund recipient is invalid.');
187
+ }
188
+ const recipientBuffer = await this.state.get(recipientAddress);
189
+ if (recipientBuffer !== null) {
190
+ const recipient = nodeEntryUtils.decode(recipientBuffer);
191
+ if (!recipient) {
192
+ throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'HTLC refund recipient account is invalid.');
193
+ }
194
+ const balance = toBalance(recipient.balance);
195
+ if (!balance || balance.asBigInt() + settlement.amount.asBigInt() > this.max_amount) {
196
+ throw new V1ProtocolError(
197
+ ResultCode.TRANSFER_RECIPIENT_BALANCE_OVERFLOW,
198
+ 'HTLC refund would overflow the refund recipient balance.'
199
+ );
200
+ }
201
+ }
202
+
203
+ return true;
204
+ }
205
+
206
+ async #validateLockUniqueness(lockId) {
207
+ const txHex = b4a.toString(lockId, 'hex');
208
+ const escrowKey = EntryType.HTLC_ESCROW + txHex;
209
+ const [transaction, signedTransaction, escrow, signedEscrow] = await Promise.all([
210
+ this.state.get(txHex),
211
+ this.state.getSigned(txHex),
212
+ this.state.get(escrowKey),
213
+ this.state.getSigned(escrowKey)
214
+ ]);
215
+ if (transaction !== null || signedTransaction !== null) {
216
+ throw new V1ProtocolError(ResultCode.TX_ALREADY_EXISTS, 'HTLC lock transaction already exists.');
217
+ }
218
+ if (escrow !== null || signedEscrow !== null) {
219
+ throw new V1ProtocolError(ResultCode.TX_ALREADY_EXISTS, 'HTLC escrow already exists.');
220
+ }
221
+ }
222
+
223
+ #validateSettlementAddresses(operation) {
224
+ for (const [field, label] of [
225
+ [operation.ca, 'claim'],
226
+ [operation.ra, 'refund'],
227
+ ...(operation.fr ? [[operation.fr, 'fee recipient']] : [])
228
+ ]) {
229
+ const address = bufferToAddress(field, this.#config.addressPrefix);
230
+ const publicKey = address && tracCryptoApi.address.decodeSafe(address);
231
+ if (
232
+ !b4a.isBuffer(publicKey) ||
233
+ publicKey.length !== PUBLIC_KEY_LENGTH ||
234
+ isZeroBuffer(publicKey)
235
+ ) {
236
+ throw new V1ProtocolError(
237
+ ResultCode.TX_INVALID_PAYLOAD,
238
+ `Invalid HTLC ${label} address.`
239
+ );
240
+ }
241
+ }
242
+ }
243
+
244
+ #validateLockAuthorizations(payload) {
245
+ const operation = payload.hlo;
246
+ const lockerAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
247
+ const lockerPublicKey = tracCryptoApi.address.decodeSafe(lockerAddress);
248
+ if (!b4a.equals(lockerPublicKey, operation.ss[0])) {
249
+ throw new V1ProtocolError(
250
+ ResultCode.TX_SIGNATURE_INVALID,
251
+ 'HTLC signer zero must be the locker identified by the operation address.'
252
+ );
253
+ }
254
+
255
+ if (!verifyOrderedHtlcCosignerSignatures(operation.cs, operation.tx, operation.ss)) {
256
+ throw new V1ProtocolError(
257
+ ResultCode.TX_SIGNATURE_INVALID,
258
+ 'HTLC cosigner signatures are invalid or not in signer-set order.'
259
+ );
260
+ }
261
+ }
262
+
263
+ async #validateRefundEpoch(operation) {
264
+ const [signedEpoch, unsignedEpoch] = await Promise.all([
265
+ this.state.getCurrentEpoch(),
266
+ this.state.getCurrentEpochUnsigned()
267
+ ]);
268
+ if (typeof signedEpoch !== 'bigint' || typeof unsignedEpoch !== 'bigint') {
269
+ throw new V1ProtocolError(
270
+ ResultCode.TX_INVALID_PAYLOAD,
271
+ 'HTLC locks require an initialized network epoch.'
272
+ );
273
+ }
274
+
275
+ const currentEpoch = signedEpoch > unsignedEpoch ? signedEpoch : unsignedEpoch;
276
+ const refundEpoch = b4a.from(operation.re).readBigUInt64BE(0);
277
+ if (refundEpoch < currentEpoch + HTLC_MIN_LOCK_DURATION_EPOCHS) {
278
+ throw new V1ProtocolError(
279
+ ResultCode.TX_INVALID_PAYLOAD,
280
+ `HTLC refund epoch must be at least ${HTLC_MIN_LOCK_DURATION_EPOCHS} epoch(s) after the current epoch.`
281
+ );
282
+ }
283
+ }
284
+
285
+ async #validateLockerBalance(payload) {
286
+ const lockerAddress = bufferToAddress(payload.address, this.#config.addressPrefix);
287
+ const lockerEntry = await this.state.getNodeEntryUnsigned(lockerAddress);
288
+ if (!lockerEntry) {
289
+ throw new V1ProtocolError(ResultCode.TRANSFER_SENDER_NOT_FOUND, 'HTLC locker account not found.');
290
+ }
291
+
292
+ const principal = bufferToBigInt(payload.hlo.am);
293
+ const surchargeFee = bufferToBigInt(payload.hlo.fa);
294
+ const escrowAmount = principal + surchargeFee;
295
+ if (escrowAmount > this.max_amount || escrowAmount + this.fee > this.max_amount) {
296
+ throw new V1ProtocolError(
297
+ ResultCode.TRANSFER_AMOUNT_TOO_LARGE,
298
+ 'HTLC escrow amount exceeds the maximum allowed value.'
299
+ );
300
+ }
301
+
302
+ if (bufferToBigInt(lockerEntry.balance) < escrowAmount + this.fee) {
303
+ throw new V1ProtocolError(
304
+ ResultCode.TRANSFER_INSUFFICIENT_BALANCE,
305
+ 'Insufficient balance for HTLC principal, surcharge fee, and network fee.'
306
+ );
307
+ }
308
+ }
309
+
310
+ async #validateValidatorRewardBalance() {
311
+ if (!this.#selfAddress) return;
312
+ const validatorEntry = await this.state.getNodeEntryUnsigned(this.#selfAddress);
313
+ if (!validatorEntry) return;
314
+
315
+ const reward = BALANCE_FEE.percentage(PERCENT_75);
316
+ if (bufferToBigInt(validatorEntry.balance) + bufferToBigInt(reward.value) > this.max_amount) {
317
+ throw new V1ProtocolError(
318
+ ResultCode.TRANSFER_RECIPIENT_BALANCE_OVERFLOW,
319
+ 'HTLC validator reward would overflow the validator balance.'
320
+ );
321
+ }
322
+ }
323
+ }
324
+
325
+ export default PartialHtlcValidator;
@@ -1,28 +1,28 @@
1
1
  import b4a from 'b4a';
2
2
  import tracCryptoApi from 'trac-crypto-api';
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';
3
+ import StateValidationSchema from '../../../../../state/validators/StateValidationSchema.js';
4
+ import {bufferToAddress} from "../../../../../state/utils/address.js";
5
+ import {createMessage} from "../../../../../../utils/buffer.js";
6
+ import {OperationType, ResultCode, PUBLIC_KEY_LENGTH} 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
10
  import {V1ProtocolError} from '../../v1/V1ProtocolError.js';
11
+ import {createHtlcLockSigningMessage} from '../../../../../../utils/htlcLock.js';
11
12
 
12
13
  const MAX_AMOUNT = BigInt('0xffffffffffffffffffffffffffffffff');
13
14
  const FEE_BIGINT = bufferToBigInt(FEE);
14
- const PUBLIC_KEY_LENGTH = 32;
15
15
 
16
16
  class PartialOperationValidator {
17
17
  #state;
18
- #check;
18
+ #stateValidationSchema;
19
19
  #config
20
20
  #selfAddress
21
21
 
22
22
  constructor(state, selfAddress, config) {
23
23
  this.#state = state;
24
24
  this.#config = config;
25
- this.#check = new Check(this.#config);
25
+ this.#stateValidationSchema = new StateValidationSchema(this.#config);
26
26
  this.max_amount = MAX_AMOUNT;
27
27
  this.fee = FEE_BIGINT;
28
28
  this.#selfAddress = selfAddress;
@@ -32,8 +32,8 @@ class PartialOperationValidator {
32
32
  return this.#state;
33
33
  }
34
34
 
35
- get check() {
36
- return this.#check;
35
+ get stateValidationSchema() {
36
+ return this.#stateValidationSchema;
37
37
  }
38
38
 
39
39
  async validate() {
@@ -45,25 +45,31 @@ class PartialOperationValidator {
45
45
  throw new V1ProtocolError(ResultCode.TX_INVALID_PAYLOAD, 'Payload or payload type is missing.');
46
46
  }
47
47
 
48
- const selectedValidator = this.#selectCheckSchemaValidator(payload.type);
48
+ const selectedValidator = this.#selectStateSchemaValidator(payload.type);
49
49
  const isPayloadValid = selectedValidator(payload);
50
50
  if (!isPayloadValid) {
51
51
  throw new V1ProtocolError(ResultCode.SCHEMA_VALIDATION_FAILED, 'Payload is invalid.');
52
52
  }
53
53
  }
54
54
 
55
- #selectCheckSchemaValidator(type) {
55
+ #selectStateSchemaValidator(type) {
56
56
  switch (type) {
57
57
  case OperationType.ADD_WRITER:
58
58
  case OperationType.REMOVE_WRITER:
59
59
  case OperationType.ADMIN_RECOVERY:
60
- return this.check.validateRoleAccessOperation.bind(this.check);
60
+ return this.stateValidationSchema.validateRoleAccessOperation.bind(this.stateValidationSchema);
61
61
  case OperationType.BOOTSTRAP_DEPLOYMENT:
62
- return this.check.validateBootstrapDeploymentOperation.bind(this.check);
62
+ return this.stateValidationSchema.validateBootstrapDeploymentOperation.bind(this.stateValidationSchema);
63
63
  case OperationType.TX:
64
- return this.check.validateTransactionOperation.bind(this.check);
64
+ return this.stateValidationSchema.validateTransactionOperation.bind(this.stateValidationSchema);
65
65
  case OperationType.TRANSFER:
66
- return this.check.validateTransferOperation.bind(this.check);
66
+ return this.stateValidationSchema.validateTransferOperation.bind(this.stateValidationSchema);
67
+ case OperationType.HTLC_LOCK:
68
+ return this.stateValidationSchema.validateHtlcLockOperation.bind(this.stateValidationSchema);
69
+ case OperationType.HTLC_CLAIM:
70
+ return this.stateValidationSchema.validateHtlcClaimOperation.bind(this.stateValidationSchema);
71
+ case OperationType.HTLC_REFUND:
72
+ return this.stateValidationSchema.validateHtlcRefundOperation.bind(this.stateValidationSchema);
67
73
  default:
68
74
  throw new V1ProtocolError(
69
75
  ResultCode.OPERATION_TYPE_UNKNOWN,
@@ -130,6 +136,23 @@ class PartialOperationValidator {
130
136
  operation.in,
131
137
  OperationType.TRANSFER
132
138
  ];
139
+ case OperationType.HTLC_CLAIM:
140
+ return [
141
+ this.#config.networkId,
142
+ operation.txv,
143
+ operation.li,
144
+ operation.pi,
145
+ operation.in,
146
+ OperationType.HTLC_CLAIM
147
+ ];
148
+ case OperationType.HTLC_REFUND:
149
+ return [
150
+ this.#config.networkId,
151
+ operation.txv,
152
+ operation.li,
153
+ operation.in,
154
+ OperationType.HTLC_REFUND
155
+ ];
133
156
  default:
134
157
  throw new V1ProtocolError(
135
158
  ResultCode.OPERATION_TYPE_UNKNOWN,
@@ -144,9 +167,9 @@ class PartialOperationValidator {
144
167
 
145
168
  const incomingPublicKey = tracCryptoApi.address.decodeSafe(bufferToAddress(payload.address, this.#config.addressPrefix));
146
169
  const incomingSignature = operation.is;
147
- const messageComponents = this.#getMessageComponents(payload);
148
-
149
- const message = createMessage(...messageComponents);
170
+ const message = payload.type === OperationType.HTLC_LOCK
171
+ ? createHtlcLockSigningMessage(this.#config.networkId, payload.address, operation)
172
+ : createMessage(...this.#getMessageComponents(payload));
150
173
  const messageHash = await tracCryptoApi.hash.blake3(message);
151
174
  const payloadHash = operation.tx;
152
175
  if (!b4a.equals(payloadHash, messageHash)) {
@@ -1,8 +1,8 @@
1
1
  import b4a from 'b4a';
2
- import {OperationType, ResultCode} from "../../../../../utils/constants.js";
3
- import {bufferToAddress} from "../../../../state/utils/address.js";
2
+ import {OperationType, ResultCode} from "../../../../../../utils/constants.js";
3
+ import {bufferToAddress} from "../../../../../state/utils/address.js";
4
4
  import PartialOperationValidator from './PartialOperationValidator.js';
5
- import {bufferToBigInt} from "../../../../../utils/amountSerialization.js";
5
+ import {bufferToBigInt} from "../../../../../../utils/amountSerialization.js";
6
6
  import {V1ProtocolError} from '../../v1/V1ProtocolError.js';
7
7
 
8
8
  class PartialRoleAccessValidator extends PartialOperationValidator {
@@ -1,8 +1,8 @@
1
1
  import b4a from 'b4a';
2
- import {safeDecodeApplyOperation} from "../../../../../utils/protobuf/operationHelpers.js";
3
- import deploymentEntryUtils from "../../../../state/utils/deploymentEntry.js";
2
+ import {safeDecodeApplyOperation} from "../../../../../../codecs/apply/applyOperationCodec.js";
3
+ import deploymentEntryUtils from "../../../../../state/utils/deploymentEntry.js";
4
4
  import PartialOperationValidator from './PartialOperationValidator.js';
5
- import {ResultCode} from "../../../../../utils/constants.js";
5
+ import {ResultCode} from "../../../../../../utils/constants.js";
6
6
  import {V1ProtocolError} from '../../v1/V1ProtocolError.js';
7
7
 
8
8
  class PartialTransactionValidator extends PartialOperationValidator {
@@ -1,7 +1,7 @@
1
- import {bufferToAddress} from "../../../../state/utils/address.js";
2
- import {bufferToBigInt} from "../../../../../utils/amountSerialization.js";
3
- import {ResultCode} from "../../../../../utils/constants.js";
4
- import { NULL_BUFFER } from '../../../../../utils/buffer.js';
1
+ import {bufferToAddress} from "../../../../../state/utils/address.js";
2
+ import {bufferToBigInt} from "../../../../../../utils/amountSerialization.js";
3
+ import {ResultCode} from "../../../../../../utils/constants.js";
4
+ import { NULL_BUFFER } from '../../../../../../utils/buffer.js';
5
5
  import PartialOperationValidator from './PartialOperationValidator.js';
6
6
  import {V1ProtocolError} from '../../v1/V1ProtocolError.js';
7
7
  import tracCryptoApi from "trac-crypto-api";
@@ -1,4 +1,4 @@
1
- import {ResultCode} from '../../../../utils/constants.js';
1
+ import {ResultCode} from '../../../../../utils/constants.js';
2
2
 
3
3
  /**
4
4
  * V1 protocol error type.