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.
- package/SECURITY.md +2 -2
- package/cli/commandHandler.js +317 -20
- package/cli/handlers.js +9 -1
- package/cli/interactive.js +4 -3
- package/cli/tokenSupplyAudit.js +90 -0
- package/msb.mjs +1 -0
- package/package.json +15 -6
- package/rpc/create_server.js +12 -3
- package/rpc/handlers.js +103 -0
- package/rpc/routes/v1.js +8 -0
- package/src/codecs/apply/applyOperationCodec.js +334 -0
- package/src/codecs/apply/applyOperations.generated.cjs +6380 -0
- package/src/codecs/consensus/v1/consensusV1.generated.cjs +3508 -0
- package/src/codecs/consensus/v1/consensusV1OperationCodec.js +161 -0
- package/src/codecs/consensus/v1/vdfConfigCodec.js +83 -0
- package/src/{utils/protobuf → codecs/network/v1}/networkV1.generated.cjs +664 -393
- package/src/codecs/network/v1/networkV1OperationCodec.js +25 -0
- package/src/codecs/protobufRuntime.cjs +95 -0
- package/src/codecs/state/escrowEntryCodec.js +45 -0
- package/src/codecs/state/state.generated.cjs +659 -0
- package/src/config/args.js +0 -1
- package/src/config/config.js +41 -7
- package/src/config/env.js +33 -9
- package/src/core/consensus/epochAppenderSelection/EpochAppenderSelectionError.js +55 -0
- package/src/core/consensus/epochAppenderSelection/EpochAppenderSelectionOperations.js +118 -0
- package/src/core/consensus/epochAppenderSelection/EpochAppenderSelector.js +1042 -0
- package/src/core/consensus/epochAppenderSelection/createEpochAppenderSelector.js +23 -0
- package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionLedgerReader.js +274 -0
- package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionState.js +488 -0
- package/src/core/consensus/epochAppenderSelection/epochAppenderSelectionStorage.js +93 -0
- package/src/core/consensus/quorum.js +11 -0
- package/src/core/consensus/services/EpochCoordinationRound.js +839 -0
- package/src/core/consensus/services/EpochCoordinatorOperations.js +211 -0
- package/src/core/consensus/services/EpochCoordinatorService.js +335 -0
- package/src/core/consensus/services/EpochRoundListeners.js +116 -0
- package/src/core/consensus/services/EpochStateMachine.js +141 -0
- package/src/core/consensus/services/VDFBare.js +291 -0
- package/src/core/consensus/services/VDFNode.js +111 -0
- package/src/core/consensus/services/VDFProgressWatchdog.js +79 -0
- package/src/core/consensus/services/VDFService.js +87 -0
- package/src/core/consensus/services/VDFServiceManager.js +147 -0
- package/src/core/consensus/services/createVDFService.js +16 -0
- package/src/core/consensus/services/vdf-guardian.js +32 -0
- package/src/core/consensus/services/vdf-worker.js +90 -0
- package/src/core/network/Network.js +327 -238
- package/src/core/network/protocols/consensus/IndexerMessages.js +72 -0
- package/src/core/network/protocols/consensus/v1/ConsensusV1ConnectionPolicy.js +57 -0
- package/src/core/network/protocols/consensus/v1/ConsensusV1MessageRouter.js +147 -0
- package/src/core/network/protocols/consensus/v1/ConsensusV1Protocol.js +72 -0
- package/src/core/network/protocols/consensus/v1/IndexerPendingRequestService.js +108 -0
- package/src/core/network/protocols/consensus/v1/V1ConsensusProtocolError.js +29 -0
- package/src/core/network/protocols/consensus/v1/handlers/V1EpochAppendOperationHandler.js +68 -0
- package/src/core/network/protocols/consensus/v1/handlers/V1EpochProofProposalOperationHandler.js +226 -0
- package/src/core/network/protocols/consensus/v1/validators/ConsensusValidationSchema.js +192 -0
- package/src/core/network/protocols/consensus/v1/validators/V1BaseConsensusOperation.js +428 -0
- package/src/core/network/protocols/consensus/v1/validators/V1EpochAppendReply.js +47 -0
- package/src/core/network/protocols/consensus/v1/validators/V1EpochAppendRequest.js +17 -0
- package/src/core/network/protocols/consensus/v1/validators/V1EpochProofProposalApproval.js +116 -0
- package/src/core/network/protocols/consensus/v1/validators/V1EpochProofProposalRequest.js +152 -0
- package/src/core/network/protocols/consensus/v1/validators/consensusValidationSchemaHelpers.js +145 -0
- package/src/core/network/protocols/shared/ConnectionOperationHandler.js +27 -0
- package/src/core/network/protocols/{NetworkMessages.js → validators/ValidatorMessages.js} +24 -14
- package/src/core/network/protocols/{ProtocolSession.js → validators/ValidatorProtocolSession.js} +22 -25
- package/src/core/network/protocols/{legacy/NetworkMessageRouter.js → validators/legacy/ValidatorLegacyMessageRouter.js} +6 -6
- package/src/core/network/protocols/{LegacyProtocol.js → validators/legacy/ValidatorLegacyProtocol.js} +9 -7
- package/src/core/network/protocols/{legacy → validators/legacy}/handlers/BaseStateOperationHandler.js +1 -1
- package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyGetRequestHandler.js +5 -5
- package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyRoleOperationHandler.js +5 -5
- package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacySubnetworkOperationHandler.js +5 -5
- package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyTransferOperationHandler.js +5 -5
- package/src/core/network/protocols/{ProtocolInterface.js → validators/shared/ValidatorProtocolInterface.js} +5 -5
- package/src/core/network/protocols/{shared → validators/shared}/validators/PartialBootstrapDeploymentValidator.js +1 -1
- package/src/core/network/protocols/validators/shared/validators/PartialHtlcValidator.js +325 -0
- package/src/core/network/protocols/{shared → validators/shared}/validators/PartialOperationValidator.js +44 -21
- package/src/core/network/protocols/{shared → validators/shared}/validators/PartialRoleAccessValidator.js +3 -3
- package/src/core/network/protocols/{shared → validators/shared}/validators/PartialTransactionValidator.js +3 -3
- package/src/core/network/protocols/{shared → validators/shared}/validators/PartialTransferValidator.js +4 -4
- package/src/core/network/protocols/{v1 → validators/v1}/V1ProtocolError.js +1 -1
- package/src/core/network/{services/PendingRequestService.js → protocols/validators/v1/ValidatorPendingRequestService.js} +19 -82
- package/src/core/network/protocols/{v1/NetworkMessageRouter.js → validators/v1/ValidatorV1MessageRouter.js} +9 -9
- package/src/core/network/protocols/{V1Protocol.js → validators/v1/ValidatorV1Protocol.js} +10 -8
- package/src/core/network/protocols/{connectionPolicies.js → validators/v1/connectionPolicies.js} +1 -1
- package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1BaseOperationHandler.js +6 -23
- package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1BroadcastTransactionOperationHandler.js +14 -9
- package/src/core/network/protocols/{v1 → validators/v1}/handlers/V1LivenessOperationHandler.js +5 -4
- package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionErrorMapper.js +3 -3
- package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionOperationStrategies.js +44 -1
- package/src/core/network/protocols/{v1 → validators/v1}/handlers/broadcastTransaction/BroadcastTransactionPayloadUtils.js +1 -1
- package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BaseOperation.js +19 -15
- package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BroadcastTransactionRequest.js +2 -2
- package/src/core/network/protocols/{v1 → validators/v1}/validators/V1BroadcastTransactionResponse.js +34 -18
- package/src/core/network/protocols/{v1 → validators/v1}/validators/V1ValidationSchema.js +3 -3
- package/src/core/network/services/IndexerConnectionManager.js +88 -0
- package/src/core/network/services/TransactionPoolService.js +11 -18
- package/src/core/network/services/TransactionRateLimiterService.js +1 -1
- package/src/core/network/services/ValidatorConnectionManager.js +163 -0
- package/src/core/network/services/{MessageOrchestrator.js → ValidatorMessageOrchestrator.js} +34 -42
- package/src/core/network/services/ValidatorObserverService.js +21 -36
- package/src/core/network/shared/BasePendingRequestService.js +67 -0
- package/src/core/network/shared/PeerConnectionManager.js +185 -0
- package/src/core/state/State.js +1789 -173
- package/src/core/state/StateEventQueue.js +69 -0
- package/src/core/state/utils/address.js +10 -6
- package/src/core/state/utils/epochProof.js +109 -0
- package/src/core/state/utils/escrowEntry.js +178 -0
- package/src/core/state/validators/StateValidationSchema.js +757 -0
- package/src/core/state/validators/stateValidationRules.js +312 -0
- package/src/index.js +255 -65
- package/src/messages/consensus/v1/ConsensusMessageBuilder.js +368 -0
- package/src/messages/consensus/v1/ConsensusMessageDirector.js +142 -0
- package/src/messages/consensus/v1/consensusMessageFactory.js +12 -0
- package/src/messages/consensus/v1/epochAppendMessage.js +58 -0
- package/src/messages/network/v1/NetworkMessageBuilder.js +15 -7
- package/src/messages/state/ApplyStateMessageBuilder.js +499 -13
- package/src/messages/state/ApplyStateMessageDirector.js +261 -2
- package/src/utils/StateMachine.js +125 -0
- package/src/utils/applyOperations.js +45 -2
- package/src/utils/buffer.js +239 -59
- package/src/utils/consensusConfig.js +76 -0
- package/src/utils/constants.js +174 -73
- package/src/utils/helpers.js +0 -25
- package/src/utils/htlcLock.js +122 -0
- package/src/utils/logger.js +10 -4
- package/src/utils/migrationUtils.js +2 -1
- package/src/utils/normalizers.js +113 -0
- package/src/utils/scheduler/SchedulableService.js +107 -0
- package/src/utils/{Scheduler.js → scheduler/Scheduler.js} +56 -17
- package/src/core/network/services/ConnectionManager.js +0 -406
- package/src/utils/check.js +0 -525
- package/src/utils/protobuf/applyOperations.cjs +0 -1373
- package/src/utils/protobuf/operationHelpers.js +0 -81
- /package/src/core/network/protocols/{legacy → validators/legacy}/handlers/LegacyResponseHandler.js +0 -0
- /package/src/core/network/protocols/{legacy → validators/legacy}/validators/ValidatorResponse.js +0 -0
- /package/src/core/network/protocols/{legacy → validators/legacy}/validators/base/BaseResponse.js +0 -0
- /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessRequest.js +0 -0
- /package/src/core/network/protocols/{v1 → validators/v1}/validators/V1LivenessResponse.js +0 -0
|
@@ -1,21 +1,41 @@
|
|
|
1
1
|
import b4a from 'b4a';
|
|
2
2
|
import tracCryptoApi from 'trac-crypto-api';
|
|
3
3
|
|
|
4
|
-
import { createMessage } from '../../utils/buffer.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { createMessage, isZeroBuffer, toHex, uint8ToBuffer } from '../../utils/buffer.js';
|
|
5
|
+
import {
|
|
6
|
+
AMOUNT_BYTE_LENGTH,
|
|
7
|
+
EPOCH_BYTE_LENGTH,
|
|
8
|
+
HASH_BYTE_LENGTH,
|
|
9
|
+
HTLC_LOCK_ID_BYTE_LENGTH,
|
|
10
|
+
HTLC_MAX_SIGNERS,
|
|
11
|
+
HTLC_METADATA_MAX_BYTE_LENGTH,
|
|
12
|
+
HTLC_PREIMAGE_BYTE_LENGTH,
|
|
13
|
+
HTLC_THRESHOLD_BYTE_LENGTH,
|
|
14
|
+
NONCE_BYTE_LENGTH,
|
|
15
|
+
OperationType,
|
|
16
|
+
PUBLIC_KEY_LENGTH,
|
|
17
|
+
SIGNATURE_BYTE_LENGTH,
|
|
18
|
+
} from '../../utils/constants.js';
|
|
19
|
+
import { addressToBuffer, bufferToAddress, isAddressValid } from '../../core/state/utils/address.js';
|
|
8
20
|
import {
|
|
9
21
|
isAdminControl,
|
|
10
22
|
isBalanceInitialization,
|
|
23
|
+
isHtlc,
|
|
11
24
|
isBootstrapDeployment,
|
|
25
|
+
isConsensusControl,
|
|
12
26
|
isCoreAdmin,
|
|
13
27
|
isRoleAccess,
|
|
28
|
+
isSetEpoch,
|
|
14
29
|
isTransaction,
|
|
15
30
|
isTransfer,
|
|
16
31
|
operationToPayload
|
|
17
32
|
} from '../../utils/applyOperations.js';
|
|
33
|
+
import { decodeConsensusConfig } from '../../codecs/apply/applyOperationCodec.js';
|
|
18
34
|
import { isHexString } from '../../utils/helpers.js';
|
|
35
|
+
import {
|
|
36
|
+
createHtlcLockSigningMessage,
|
|
37
|
+
verifyOrderedHtlcCosignerSignatures
|
|
38
|
+
} from '../../utils/htlcLock.js';
|
|
19
39
|
|
|
20
40
|
// Single use per transaction: reuse of this instance needs mutex/queue or fail-fast and can delay validation or break validation rule.
|
|
21
41
|
// A fresh instance is effectively zero-cost, so no reset() is provided.
|
|
@@ -28,25 +48,42 @@ import { isHexString } from '../../utils/helpers.js';
|
|
|
28
48
|
class ApplyStateMessageBuilder {
|
|
29
49
|
#address;
|
|
30
50
|
#amount;
|
|
51
|
+
#built = false;
|
|
31
52
|
#channel;
|
|
32
|
-
#config
|
|
33
53
|
#contentHash;
|
|
54
|
+
#config;
|
|
55
|
+
#consensusConfig;
|
|
56
|
+
#encodedConsensusConfig;
|
|
34
57
|
#externalBootstrap;
|
|
58
|
+
#htlcLockId;
|
|
59
|
+
#htlcPreimage;
|
|
35
60
|
#incomingAddress;
|
|
36
61
|
#incomingNonce;
|
|
37
62
|
#incomingSignature;
|
|
38
63
|
#incomingWriterKey;
|
|
64
|
+
#htlcCosignerSignatures = [];
|
|
65
|
+
#htlcClaimAddress;
|
|
66
|
+
#htlcFeeAmount;
|
|
67
|
+
#htlcFeeRecipient;
|
|
68
|
+
#htlcHashLock;
|
|
69
|
+
#htlcMetadata;
|
|
70
|
+
#htlcRefundAddress;
|
|
71
|
+
#htlcRefundEpoch;
|
|
72
|
+
#htlcSignerSet;
|
|
73
|
+
#htlcThreshold;
|
|
39
74
|
#msbBootstrap;
|
|
75
|
+
#nonce;
|
|
40
76
|
#operationType;
|
|
77
|
+
#output;
|
|
41
78
|
#payload;
|
|
79
|
+
#payloadKey;
|
|
80
|
+
#phase;
|
|
42
81
|
#txHash;
|
|
43
82
|
#txValidity;
|
|
44
83
|
#wallet;
|
|
45
84
|
#writingKey;
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#payloadKey;
|
|
49
|
-
#built=false;
|
|
85
|
+
#epochData;
|
|
86
|
+
#epochSchemaVersion;
|
|
50
87
|
|
|
51
88
|
constructor(wallet, config) {
|
|
52
89
|
this.#config = config;
|
|
@@ -154,8 +191,127 @@ class ApplyStateMessageBuilder {
|
|
|
154
191
|
return this;
|
|
155
192
|
}
|
|
156
193
|
|
|
194
|
+
setHtlcLockId(lockId) {
|
|
195
|
+
this.#htlcLockId = this.#normalizeHexBuffer(
|
|
196
|
+
lockId,
|
|
197
|
+
HTLC_LOCK_ID_BYTE_LENGTH,
|
|
198
|
+
'HTLC lock ID'
|
|
199
|
+
);
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
setHtlcPreimage(preimage) {
|
|
204
|
+
this.#htlcPreimage = this.#normalizeHexBuffer(
|
|
205
|
+
preimage,
|
|
206
|
+
HTLC_PREIMAGE_BYTE_LENGTH,
|
|
207
|
+
'HTLC preimage'
|
|
208
|
+
);
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
157
212
|
setAmount(amount) {
|
|
158
|
-
this.#amount = this.#normalizeHexBuffer(amount,
|
|
213
|
+
this.#amount = this.#normalizeHexBuffer(amount, AMOUNT_BYTE_LENGTH, 'Amount');
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
setEpochData(schemaVersion, data) {
|
|
218
|
+
this.#epochSchemaVersion = this.#normalizeHexBuffer(schemaVersion, 1, 'Epoch schema version');
|
|
219
|
+
this.#epochData = this.#normalizeBytesBuffer(data, 'Epoch data');
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
setNonce(nonce) {
|
|
224
|
+
this.#nonce = this.#normalizeHexBuffer(nonce, NONCE_BYTE_LENGTH, 'Nonce');
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
setHtlcClaimAddress(address) {
|
|
229
|
+
const addressBuffer = this.#normalizeAddress(address);
|
|
230
|
+
if (!addressBuffer) throw new Error('HTLC claim address must be a valid TRAC address.');
|
|
231
|
+
this.#htlcClaimAddress = addressBuffer;
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
setHtlcRefundAddress(address) {
|
|
236
|
+
const addressBuffer = this.#normalizeAddress(address);
|
|
237
|
+
if (!addressBuffer) throw new Error('HTLC refund address must be a valid TRAC address.');
|
|
238
|
+
this.#htlcRefundAddress = addressBuffer;
|
|
239
|
+
return this;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
setHtlcFeeAmount(amount) {
|
|
243
|
+
this.#htlcFeeAmount = this.#normalizeHexBuffer(amount, AMOUNT_BYTE_LENGTH, 'HTLC fee amount');
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
setHtlcFeeRecipient(address) {
|
|
248
|
+
if (address === undefined || address === null) {
|
|
249
|
+
this.#htlcFeeRecipient = undefined;
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
const addressBuffer = this.#normalizeAddress(address);
|
|
253
|
+
if (!addressBuffer) throw new Error('HTLC fee recipient must be a valid TRAC address.');
|
|
254
|
+
this.#htlcFeeRecipient = addressBuffer;
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
setHtlcHashLock(hashLock) {
|
|
259
|
+
this.#htlcHashLock = this.#normalizeHexBuffer(hashLock, HASH_BYTE_LENGTH, 'HTLC hashlock');
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
setHtlcRefundEpoch(refundEpoch) {
|
|
264
|
+
this.#htlcRefundEpoch = this.#normalizeHexBuffer(refundEpoch, EPOCH_BYTE_LENGTH, 'HTLC refund epoch');
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
setHtlcMetadata(metadata) {
|
|
269
|
+
if (metadata === undefined) {
|
|
270
|
+
this.#htlcMetadata = undefined;
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
const bytes = this.#normalizeBytesBuffer(metadata, 'HTLC metadata');
|
|
274
|
+
if (bytes.length > HTLC_METADATA_MAX_BYTE_LENGTH) {
|
|
275
|
+
throw new Error(`HTLC metadata must not exceed ${HTLC_METADATA_MAX_BYTE_LENGTH} bytes.`);
|
|
276
|
+
}
|
|
277
|
+
this.#htlcMetadata = b4a.from(bytes);
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
setHtlcSignerSet(signerSet) {
|
|
282
|
+
if (!Array.isArray(signerSet) || signerSet.length < 1 || signerSet.length > HTLC_MAX_SIGNERS) {
|
|
283
|
+
throw new Error(`HTLC signer set must contain between 1 and ${HTLC_MAX_SIGNERS} public keys.`);
|
|
284
|
+
}
|
|
285
|
+
this.#htlcSignerSet = signerSet.map((signer, index) => {
|
|
286
|
+
const publicKey = this.#normalizeHexBuffer(signer, PUBLIC_KEY_LENGTH, `HTLC signer ${index}`);
|
|
287
|
+
if (isZeroBuffer(publicKey)) throw new Error(`HTLC signer ${index} must not be zero-filled.`);
|
|
288
|
+
return publicKey;
|
|
289
|
+
});
|
|
290
|
+
return this;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
setHtlcThreshold(threshold) {
|
|
294
|
+
this.#htlcThreshold = Number.isInteger(threshold)
|
|
295
|
+
? uint8ToBuffer(threshold)
|
|
296
|
+
: this.#normalizeHexBuffer(threshold, HTLC_THRESHOLD_BYTE_LENGTH, 'HTLC threshold');
|
|
297
|
+
return this;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
setHtlcCosignerSignatures(signatures = []) {
|
|
301
|
+
if (!Array.isArray(signatures) || signatures.length > HTLC_MAX_SIGNERS - 1) {
|
|
302
|
+
throw new Error(`HTLC cosigner signatures must be an array with at most ${HTLC_MAX_SIGNERS - 1} entries.`);
|
|
303
|
+
}
|
|
304
|
+
this.#htlcCosignerSignatures = signatures.map((signature, index) => this.#normalizeHexBuffer(
|
|
305
|
+
signature,
|
|
306
|
+
SIGNATURE_BYTE_LENGTH,
|
|
307
|
+
`HTLC cosigner signature ${index}`
|
|
308
|
+
));
|
|
309
|
+
return this;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
setConsensusConfig(encodedConsensusConfig) {
|
|
313
|
+
this.#consensusConfig = decodeConsensusConfig(encodedConsensusConfig);
|
|
314
|
+
this.#encodedConsensusConfig = encodedConsensusConfig;
|
|
159
315
|
return this;
|
|
160
316
|
}
|
|
161
317
|
|
|
@@ -240,6 +396,24 @@ class ApplyStateMessageBuilder {
|
|
|
240
396
|
throw new Error(`${fieldName} must be a ${expectedBytes}-byte buffer or ${expectedBytes * 2}-length hexstring.`);
|
|
241
397
|
}
|
|
242
398
|
|
|
399
|
+
#normalizeBytesBuffer(value, fieldName) {
|
|
400
|
+
if (b4a.isBuffer(value)) {
|
|
401
|
+
if (value.length === 0) {
|
|
402
|
+
throw new Error(`${fieldName} must be a non-empty buffer.`);
|
|
403
|
+
}
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (typeof value === 'string') {
|
|
408
|
+
if (!isHexString(value)) {
|
|
409
|
+
throw new Error(`${fieldName} must be a non-empty hexstring.`);
|
|
410
|
+
}
|
|
411
|
+
return b4a.from(value, 'hex');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
throw new Error(`${fieldName} must be a non-empty buffer or hexstring.`);
|
|
415
|
+
}
|
|
416
|
+
|
|
243
417
|
#normalizeAddress(address) {
|
|
244
418
|
if (b4a.isBuffer(address)) {
|
|
245
419
|
const addr = bufferToAddress(address, this.#config.addressPrefix);
|
|
@@ -251,16 +425,136 @@ class ApplyStateMessageBuilder {
|
|
|
251
425
|
return addressToBuffer(address, this.#config.addressPrefix);
|
|
252
426
|
}
|
|
253
427
|
|
|
428
|
+
#getHtlcLockFields() {
|
|
429
|
+
this.#requireFields([
|
|
430
|
+
[this.#txValidity, 'Transaction validity'],
|
|
431
|
+
[this.#htlcClaimAddress, 'HTLC claim address'],
|
|
432
|
+
[this.#htlcRefundAddress, 'HTLC refund address'],
|
|
433
|
+
[this.#amount, 'Amount'],
|
|
434
|
+
[this.#htlcFeeAmount, 'HTLC fee amount'],
|
|
435
|
+
[this.#htlcHashLock, 'HTLC hashlock'],
|
|
436
|
+
[this.#htlcRefundEpoch, 'HTLC refund epoch'],
|
|
437
|
+
[this.#htlcSignerSet, 'HTLC signer set'],
|
|
438
|
+
[this.#htlcThreshold, 'HTLC threshold']
|
|
439
|
+
]);
|
|
440
|
+
|
|
441
|
+
const feeIsZero = this.#htlcFeeAmount.every(byte => byte === 0);
|
|
442
|
+
if (feeIsZero && this.#htlcFeeRecipient) {
|
|
443
|
+
throw new Error('HTLC fee recipient must be omitted when the fee amount is zero.');
|
|
444
|
+
}
|
|
445
|
+
if (!feeIsZero && !this.#htlcFeeRecipient) {
|
|
446
|
+
throw new Error('HTLC fee recipient must be set when the fee amount is non-zero.');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const lockerAddress = bufferToAddress(this.#address, this.#config.addressPrefix);
|
|
450
|
+
const lockerPublicKey = tracCryptoApi.address.decodeSafe(lockerAddress);
|
|
451
|
+
if (!b4a.isBuffer(lockerPublicKey) || !b4a.equals(lockerPublicKey, this.#htlcSignerSet[0])) {
|
|
452
|
+
throw new Error('HTLC signer zero must be the locker identified by the operation address.');
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const signerKeys = new Set();
|
|
456
|
+
for (let index = 0; index < this.#htlcSignerSet.length; index++) {
|
|
457
|
+
const signerKey = toHex(this.#htlcSignerSet[index]);
|
|
458
|
+
if (signerKeys.has(signerKey)) {
|
|
459
|
+
throw new Error('HTLC signer set must not contain duplicate public keys.');
|
|
460
|
+
}
|
|
461
|
+
signerKeys.add(signerKey);
|
|
462
|
+
|
|
463
|
+
if (index > 1 && b4a.compare(this.#htlcSignerSet[index - 1], this.#htlcSignerSet[index]) >= 0) {
|
|
464
|
+
throw new Error('HTLC cosigner public keys must be sorted by raw bytes.');
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const threshold = this.#htlcThreshold.readUInt8(0);
|
|
469
|
+
if (threshold < 1 || threshold > this.#htlcSignerSet.length) {
|
|
470
|
+
throw new Error('HTLC threshold must be between one and the signer-set size.');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (this.#htlcCosignerSignatures.length > this.#htlcSignerSet.length - 1) {
|
|
474
|
+
throw new Error('HTLC cosigner signatures cannot outnumber the cosigner keys.');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (1 + this.#htlcCosignerSignatures.length < threshold) {
|
|
478
|
+
throw new Error('HTLC signatures do not satisfy the declared signature threshold.');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
txv: this.#txValidity,
|
|
483
|
+
ca: this.#htlcClaimAddress,
|
|
484
|
+
ra: this.#htlcRefundAddress,
|
|
485
|
+
am: this.#amount,
|
|
486
|
+
fa: this.#htlcFeeAmount,
|
|
487
|
+
...(this.#htlcFeeRecipient && {fr: this.#htlcFeeRecipient}),
|
|
488
|
+
hl: this.#htlcHashLock,
|
|
489
|
+
re: this.#htlcRefundEpoch,
|
|
490
|
+
...(this.#htlcMetadata !== undefined && {md: this.#htlcMetadata}),
|
|
491
|
+
ss: this.#htlcSignerSet,
|
|
492
|
+
th: this.#htlcThreshold,
|
|
493
|
+
cs: this.#htlcCosignerSignatures
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
#validateHtlcLockSignatures(tx, lockerSignature) {
|
|
498
|
+
if (!tracCryptoApi.signature.verify(lockerSignature, tx, this.#htlcSignerSet[0])) {
|
|
499
|
+
throw new Error('HTLC locker signature is invalid.');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (!verifyOrderedHtlcCosignerSignatures(this.#htlcCosignerSignatures, tx, this.#htlcSignerSet)) {
|
|
503
|
+
throw new Error('HTLC cosigner signatures are invalid or not in signer-set order.');
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
254
507
|
async #buildPartialBody() {
|
|
255
508
|
if (!isRoleAccess(this.#operationType) && !isTransaction(this.#operationType) &&
|
|
256
|
-
!isBootstrapDeployment(this.#operationType) && !isTransfer(this.#operationType)
|
|
509
|
+
!isBootstrapDeployment(this.#operationType) && !isTransfer(this.#operationType) &&
|
|
510
|
+
!isHtlc(this.#operationType)) {
|
|
257
511
|
throw new Error(`Operation type ${this.#operationType} is not supported for partial build.`);
|
|
258
512
|
}
|
|
259
513
|
|
|
260
|
-
const nonce = tracCryptoApi.nonce.generate();
|
|
514
|
+
const nonce = this.#nonce ?? tracCryptoApi.nonce.generate();
|
|
261
515
|
let msg;
|
|
516
|
+
let htlcLockFields;
|
|
262
517
|
|
|
263
518
|
switch (this.#operationType) {
|
|
519
|
+
case OperationType.HTLC_LOCK:
|
|
520
|
+
if (this.#htlcCosignerSignatures.length > 0 && !this.#nonce) {
|
|
521
|
+
throw new Error('HTLC nonce must be supplied when the lock includes cosigner signatures.');
|
|
522
|
+
}
|
|
523
|
+
htlcLockFields = this.#getHtlcLockFields();
|
|
524
|
+
msg = createHtlcLockSigningMessage(
|
|
525
|
+
this.#config.networkId,
|
|
526
|
+
this.#address,
|
|
527
|
+
{...htlcLockFields, in: nonce}
|
|
528
|
+
);
|
|
529
|
+
break;
|
|
530
|
+
case OperationType.HTLC_REFUND:
|
|
531
|
+
this.#requireFields([
|
|
532
|
+
[this.#txValidity, 'Transaction validity'],
|
|
533
|
+
[this.#htlcLockId, 'HTLC lock ID']
|
|
534
|
+
]);
|
|
535
|
+
msg = createMessage(
|
|
536
|
+
this.#config.networkId,
|
|
537
|
+
this.#txValidity,
|
|
538
|
+
this.#htlcLockId,
|
|
539
|
+
nonce,
|
|
540
|
+
OperationType.HTLC_REFUND
|
|
541
|
+
);
|
|
542
|
+
break;
|
|
543
|
+
case OperationType.HTLC_CLAIM:
|
|
544
|
+
this.#requireFields([
|
|
545
|
+
[this.#txValidity, 'Transaction validity'],
|
|
546
|
+
[this.#htlcLockId, 'HTLC lock ID'],
|
|
547
|
+
[this.#htlcPreimage, 'HTLC preimage']
|
|
548
|
+
]);
|
|
549
|
+
msg = createMessage(
|
|
550
|
+
this.#config.networkId,
|
|
551
|
+
this.#txValidity,
|
|
552
|
+
this.#htlcLockId,
|
|
553
|
+
this.#htlcPreimage,
|
|
554
|
+
nonce,
|
|
555
|
+
OperationType.HTLC_CLAIM
|
|
556
|
+
);
|
|
557
|
+
break;
|
|
264
558
|
case OperationType.ADD_WRITER:
|
|
265
559
|
case OperationType.REMOVE_WRITER:
|
|
266
560
|
case OperationType.ADMIN_RECOVERY:
|
|
@@ -325,6 +619,7 @@ class ApplyStateMessageBuilder {
|
|
|
325
619
|
OperationType.TRANSFER
|
|
326
620
|
);
|
|
327
621
|
break;
|
|
622
|
+
|
|
328
623
|
default:
|
|
329
624
|
throw new Error(`Unsupported operation type: ${this.#operationType}`);
|
|
330
625
|
}
|
|
@@ -332,6 +627,10 @@ class ApplyStateMessageBuilder {
|
|
|
332
627
|
const tx = await tracCryptoApi.hash.blake3(msg);
|
|
333
628
|
const signature = this.#wallet.sign(tx);
|
|
334
629
|
|
|
630
|
+
if (this.#operationType === OperationType.HTLC_LOCK) {
|
|
631
|
+
this.#validateHtlcLockSignatures(tx, signature);
|
|
632
|
+
}
|
|
633
|
+
|
|
335
634
|
if (isBootstrapDeployment(this.#operationType)) {
|
|
336
635
|
return {
|
|
337
636
|
tx,
|
|
@@ -373,15 +672,111 @@ class ApplyStateMessageBuilder {
|
|
|
373
672
|
is: signature
|
|
374
673
|
};
|
|
375
674
|
}
|
|
675
|
+
if (this.#operationType === OperationType.HTLC_LOCK) {
|
|
676
|
+
return {
|
|
677
|
+
tx,
|
|
678
|
+
...htlcLockFields,
|
|
679
|
+
in: nonce,
|
|
680
|
+
is: signature
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
if (this.#operationType === OperationType.HTLC_CLAIM) {
|
|
684
|
+
return {
|
|
685
|
+
tx,
|
|
686
|
+
txv: this.#txValidity,
|
|
687
|
+
li: this.#htlcLockId,
|
|
688
|
+
pi: this.#htlcPreimage,
|
|
689
|
+
in: nonce,
|
|
690
|
+
is: signature
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
if (this.#operationType === OperationType.HTLC_REFUND) {
|
|
694
|
+
return {
|
|
695
|
+
tx,
|
|
696
|
+
txv: this.#txValidity,
|
|
697
|
+
li: this.#htlcLockId,
|
|
698
|
+
in: nonce,
|
|
699
|
+
is: signature
|
|
700
|
+
};
|
|
701
|
+
}
|
|
376
702
|
|
|
377
703
|
throw new Error(`No corresponding value type for operation: ${this.#operationType}`);
|
|
378
704
|
}
|
|
379
705
|
|
|
380
706
|
async #buildCompleteBody() {
|
|
707
|
+
if (isSetEpoch(this.#operationType)) {
|
|
708
|
+
this.#requireFields([
|
|
709
|
+
[this.#epochSchemaVersion, 'Epoch schema version'],
|
|
710
|
+
[this.#epochData, 'Epoch data']
|
|
711
|
+
]);
|
|
712
|
+
return {
|
|
713
|
+
sv: this.#epochSchemaVersion,
|
|
714
|
+
data: this.#epochData
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
381
718
|
const nonce = tracCryptoApi.nonce.generate();
|
|
382
719
|
let msg;
|
|
720
|
+
let htlcLockFields;
|
|
383
721
|
|
|
384
722
|
switch (this.#operationType) {
|
|
723
|
+
case OperationType.HTLC_CLAIM:
|
|
724
|
+
this.#requireFields([
|
|
725
|
+
[this.#txHash, 'Transaction hash'],
|
|
726
|
+
[this.#txValidity, 'Transaction validity'],
|
|
727
|
+
[this.#htlcLockId, 'HTLC lock ID'],
|
|
728
|
+
[this.#htlcPreimage, 'HTLC preimage'],
|
|
729
|
+
[this.#incomingNonce, 'Incoming nonce'],
|
|
730
|
+
[this.#incomingSignature, 'Incoming signature']
|
|
731
|
+
]);
|
|
732
|
+
msg = createMessage(
|
|
733
|
+
this.#config.networkId,
|
|
734
|
+
this.#txHash,
|
|
735
|
+
nonce,
|
|
736
|
+
this.#operationType
|
|
737
|
+
);
|
|
738
|
+
break;
|
|
739
|
+
case OperationType.HTLC_LOCK:
|
|
740
|
+
this.#requireFields([
|
|
741
|
+
[this.#txHash, 'Transaction hash'],
|
|
742
|
+
[this.#incomingNonce, 'Incoming nonce'],
|
|
743
|
+
[this.#incomingSignature, 'Incoming signature']
|
|
744
|
+
]);
|
|
745
|
+
htlcLockFields = this.#getHtlcLockFields();
|
|
746
|
+
{
|
|
747
|
+
const incomingMessage = createHtlcLockSigningMessage(
|
|
748
|
+
this.#config.networkId,
|
|
749
|
+
this.#address,
|
|
750
|
+
{...htlcLockFields, in: this.#incomingNonce}
|
|
751
|
+
);
|
|
752
|
+
const incomingHash = await tracCryptoApi.hash.blake3(incomingMessage);
|
|
753
|
+
if (!b4a.equals(incomingHash, this.#txHash)) {
|
|
754
|
+
throw new Error('Regenerated HTLC lock transaction does not match the incoming hash.');
|
|
755
|
+
}
|
|
756
|
+
this.#validateHtlcLockSignatures(this.#txHash, this.#incomingSignature);
|
|
757
|
+
}
|
|
758
|
+
msg = createMessage(
|
|
759
|
+
this.#config.networkId,
|
|
760
|
+
this.#txHash,
|
|
761
|
+
nonce,
|
|
762
|
+
this.#operationType
|
|
763
|
+
);
|
|
764
|
+
break;
|
|
765
|
+
case OperationType.HTLC_REFUND:
|
|
766
|
+
this.#requireFields([
|
|
767
|
+
[this.#txHash, 'Transaction hash'],
|
|
768
|
+
[this.#txValidity, 'Transaction validity'],
|
|
769
|
+
[this.#htlcLockId, 'HTLC lock ID'],
|
|
770
|
+
[this.#incomingNonce, 'Incoming nonce'],
|
|
771
|
+
[this.#incomingSignature, 'Incoming signature']
|
|
772
|
+
]);
|
|
773
|
+
msg = createMessage(
|
|
774
|
+
this.#config.networkId,
|
|
775
|
+
this.#txHash,
|
|
776
|
+
nonce,
|
|
777
|
+
OperationType.HTLC_REFUND
|
|
778
|
+
);
|
|
779
|
+
break;
|
|
385
780
|
case OperationType.ADD_ADMIN:
|
|
386
781
|
case OperationType.DISABLE_INITIALIZATION:
|
|
387
782
|
this.#requireFields([
|
|
@@ -499,6 +894,20 @@ class ApplyStateMessageBuilder {
|
|
|
499
894
|
this.#operationType
|
|
500
895
|
);
|
|
501
896
|
break;
|
|
897
|
+
case OperationType.SET_GENESIS_EPOCH:
|
|
898
|
+
case OperationType.SET_CONSENSUS_CONFIG:
|
|
899
|
+
this.#requireFields([
|
|
900
|
+
[this.#txValidity, 'Transaction validity'],
|
|
901
|
+
[this.#encodedConsensusConfig, 'Consensus config']
|
|
902
|
+
]);
|
|
903
|
+
msg = createMessage(
|
|
904
|
+
this.#config.networkId,
|
|
905
|
+
this.#txValidity,
|
|
906
|
+
this.#encodedConsensusConfig,
|
|
907
|
+
nonce,
|
|
908
|
+
this.#operationType
|
|
909
|
+
);
|
|
910
|
+
break;
|
|
502
911
|
default:
|
|
503
912
|
throw new Error(`Unsupported operation type: ${this.#operationType}`);
|
|
504
913
|
}
|
|
@@ -578,6 +987,42 @@ class ApplyStateMessageBuilder {
|
|
|
578
987
|
vs: signature
|
|
579
988
|
};
|
|
580
989
|
}
|
|
990
|
+
if (this.#operationType === OperationType.HTLC_CLAIM) {
|
|
991
|
+
return {
|
|
992
|
+
tx: this.#txHash,
|
|
993
|
+
txv: this.#txValidity,
|
|
994
|
+
li: this.#htlcLockId,
|
|
995
|
+
pi: this.#htlcPreimage,
|
|
996
|
+
in: this.#incomingNonce,
|
|
997
|
+
is: this.#incomingSignature,
|
|
998
|
+
va: validatorAddress,
|
|
999
|
+
vn: nonce,
|
|
1000
|
+
vs: signature
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
if (this.#operationType === OperationType.HTLC_LOCK) {
|
|
1004
|
+
return {
|
|
1005
|
+
tx: this.#txHash,
|
|
1006
|
+
...htlcLockFields,
|
|
1007
|
+
in: this.#incomingNonce,
|
|
1008
|
+
is: this.#incomingSignature,
|
|
1009
|
+
va: validatorAddress,
|
|
1010
|
+
vn: nonce,
|
|
1011
|
+
vs: signature
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
if (this.#operationType === OperationType.HTLC_REFUND) {
|
|
1015
|
+
return {
|
|
1016
|
+
tx: this.#txHash,
|
|
1017
|
+
txv: this.#txValidity,
|
|
1018
|
+
li: this.#htlcLockId,
|
|
1019
|
+
in: this.#incomingNonce,
|
|
1020
|
+
is: this.#incomingSignature,
|
|
1021
|
+
va: validatorAddress,
|
|
1022
|
+
vn: nonce,
|
|
1023
|
+
vs: signature
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
581
1026
|
if (isBalanceInitialization(this.#operationType)) {
|
|
582
1027
|
return {
|
|
583
1028
|
tx,
|
|
@@ -588,12 +1033,20 @@ class ApplyStateMessageBuilder {
|
|
|
588
1033
|
is: signature
|
|
589
1034
|
};
|
|
590
1035
|
}
|
|
1036
|
+
if (isConsensusControl(this.#operationType)) {
|
|
1037
|
+
return {
|
|
1038
|
+
tx,
|
|
1039
|
+
txv: this.#txValidity,
|
|
1040
|
+
cc: this.#consensusConfig,
|
|
1041
|
+
in: nonce,
|
|
1042
|
+
is: signature
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
591
1045
|
|
|
592
1046
|
throw new Error(`No corresponding value type for operation: ${this.#operationType}`);
|
|
593
1047
|
}
|
|
594
1048
|
|
|
595
1049
|
#encodePayloadJson(payload) {
|
|
596
|
-
const toHex = buffer => buffer.toString('hex');
|
|
597
1050
|
const address = bufferToAddress(payload.address, this.#config.addressPrefix);
|
|
598
1051
|
if (!address) {
|
|
599
1052
|
throw new Error('Payload address is invalid.');
|
|
@@ -652,6 +1105,39 @@ class ApplyStateMessageBuilder {
|
|
|
652
1105
|
is: toHex(body.is)
|
|
653
1106
|
}
|
|
654
1107
|
};
|
|
1108
|
+
case 'hlo': {
|
|
1109
|
+
const htlcLock = {
|
|
1110
|
+
tx: toHex(body.tx),
|
|
1111
|
+
txv: toHex(body.txv),
|
|
1112
|
+
ca: bufferToAddress(body.ca, this.#config.addressPrefix),
|
|
1113
|
+
ra: bufferToAddress(body.ra, this.#config.addressPrefix),
|
|
1114
|
+
am: toHex(body.am),
|
|
1115
|
+
fa: toHex(body.fa),
|
|
1116
|
+
hl: toHex(body.hl),
|
|
1117
|
+
re: toHex(body.re),
|
|
1118
|
+
cc: toHex(body.cc),
|
|
1119
|
+
ss: body.ss.map(toHex),
|
|
1120
|
+
th: toHex(body.th),
|
|
1121
|
+
cs: body.cs.map(toHex),
|
|
1122
|
+
in: toHex(body.in),
|
|
1123
|
+
is: toHex(body.is)
|
|
1124
|
+
};
|
|
1125
|
+
if (body.fr) htlcLock.fr = bufferToAddress(body.fr, this.#config.addressPrefix);
|
|
1126
|
+
if (body.md !== undefined) htlcLock.md = toHex(body.md);
|
|
1127
|
+
|
|
1128
|
+
return {...base, hlo: htlcLock};
|
|
1129
|
+
}
|
|
1130
|
+
case 'hro':
|
|
1131
|
+
return {
|
|
1132
|
+
...base,
|
|
1133
|
+
hro: {
|
|
1134
|
+
tx: toHex(body.tx),
|
|
1135
|
+
txv: toHex(body.txv),
|
|
1136
|
+
li: toHex(body.li),
|
|
1137
|
+
in: toHex(body.in),
|
|
1138
|
+
is: toHex(body.is)
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
655
1141
|
default:
|
|
656
1142
|
throw new Error(`JSON output is not supported for payload ${this.#payloadKey}.`);
|
|
657
1143
|
}
|