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
package/rpc/create_server.js
CHANGED
|
@@ -45,11 +45,20 @@ export const createServer = (msbInstance, config) => {
|
|
|
45
45
|
const requestParts = requestPath.split('/');
|
|
46
46
|
const routeParts = routeBase.split('/');
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const exact = route.exact || routeParts.some(part => part.startsWith(':'));
|
|
49
|
+
const params = {};
|
|
50
|
+
if (req.method === route.method &&
|
|
51
|
+
(exact ? requestParts.length === routeParts.length : requestParts.length >= routeParts.length) &&
|
|
52
|
+
routeParts.every((part, i) => {
|
|
53
|
+
if (part.startsWith(':') && requestParts[i]) {
|
|
54
|
+
params[part.slice(1)] = requestParts[i];
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return part === requestParts[i];
|
|
58
|
+
})) {
|
|
51
59
|
|
|
52
60
|
foundRoute = true;
|
|
61
|
+
req.params = params;
|
|
53
62
|
try {
|
|
54
63
|
// This try/catch covers synchronous errors and errors from awaited promises
|
|
55
64
|
// within the route.handler function.
|
package/rpc/handlers.js
CHANGED
|
@@ -13,6 +13,7 @@ import { bufferToBigInt, licenseBufferToBigInt } from "../src/utils/amountSerial
|
|
|
13
13
|
import { isAddressValid } from "../src/core/state/utils/address.js";
|
|
14
14
|
import { getConfirmedParameter } from "./utils/confirmedParameter.js";
|
|
15
15
|
import { BroadcastError, ValidationError } from "../src/utils/errors.js";
|
|
16
|
+
import { OperationType } from "../src/utils/constants.js";
|
|
16
17
|
|
|
17
18
|
export async function handleHealth({ msbInstance, respond }) {
|
|
18
19
|
try {
|
|
@@ -46,6 +47,108 @@ export async function handleBalance({ req, respond, msbInstance }) {
|
|
|
46
47
|
respond(200, { address, balance });
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
function readHtlcPayload(req, respond) {
|
|
51
|
+
return new Promise(resolve => {
|
|
52
|
+
let body = '';
|
|
53
|
+
let bytesRead = 0;
|
|
54
|
+
let finished = false;
|
|
55
|
+
|
|
56
|
+
req.on('data', chunk => {
|
|
57
|
+
if (finished) return;
|
|
58
|
+
bytesRead += chunk.length;
|
|
59
|
+
if (bytesRead > 2_000_000) {
|
|
60
|
+
finished = true;
|
|
61
|
+
respond(413, { error: 'Payload too large.' });
|
|
62
|
+
req.resume();
|
|
63
|
+
resolve(null);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
body += chunk.toString();
|
|
67
|
+
});
|
|
68
|
+
req.on('end', () => {
|
|
69
|
+
if (finished) return;
|
|
70
|
+
finished = true;
|
|
71
|
+
try {
|
|
72
|
+
const request = JSON.parse(body);
|
|
73
|
+
if (!request?.payload) throw new ValidationError('Payload is missing.');
|
|
74
|
+
if (typeof request.payload !== 'string' || !isBase64(request.payload)) {
|
|
75
|
+
throw new ValidationError('Payload must be a valid base64 string.');
|
|
76
|
+
}
|
|
77
|
+
const payload = decodeBase64Payload(request.payload);
|
|
78
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
79
|
+
throw new ValidationError('Invalid HTLC operation payload.');
|
|
80
|
+
}
|
|
81
|
+
resolve(payload);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
respond(400, { error: error instanceof ValidationError ? error.message : 'Invalid JSON payload.' });
|
|
84
|
+
resolve(null);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
req.on('error', () => {
|
|
88
|
+
if (finished) return;
|
|
89
|
+
finished = true;
|
|
90
|
+
respond(500, { error: 'Request stream failed during body transfer.' });
|
|
91
|
+
resolve(null);
|
|
92
|
+
});
|
|
93
|
+
req.on('aborted', () => {
|
|
94
|
+
finished = true;
|
|
95
|
+
resolve(null);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function handleHtlcSubmission({ req, msbInstance, respond }, type, operationKey, submit) {
|
|
101
|
+
const payload = await readHtlcPayload(req, respond);
|
|
102
|
+
if (!payload) return;
|
|
103
|
+
if (payload.type !== type || !payload[operationKey] || typeof payload.address !== 'string') {
|
|
104
|
+
return respond(400, { error: 'Invalid HTLC operation payload.' });
|
|
105
|
+
}
|
|
106
|
+
if (type !== OperationType.HTLC_LOCK) {
|
|
107
|
+
const lockId = req.params?.id;
|
|
108
|
+
if (!isValidTxHash(lockId) || typeof payload[operationKey].li !== 'string' ||
|
|
109
|
+
payload[operationKey].li.toLowerCase() !== lockId.toLowerCase()) {
|
|
110
|
+
return respond(400, { error: 'HTLC lock ID does not match the payload.' });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const result = await submit.call(msbInstance, payload);
|
|
115
|
+
respond(200, { result: { ...result, message: 'Transaction broadcasted successfully.' } });
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error instanceof ValidationError) return respond(400, { error: error.message });
|
|
118
|
+
if (error instanceof BroadcastError) return respond(429, { error: error.message });
|
|
119
|
+
console.error('Error in HTLC submission:', error);
|
|
120
|
+
respond(500, { error: 'An error occurred processing the transaction.' });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function handleHtlcLock(context) {
|
|
125
|
+
return handleHtlcSubmission(context, OperationType.HTLC_LOCK, 'hlo', context.msbInstance.htlcLock);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function handleHtlcClaim(context) {
|
|
129
|
+
return handleHtlcSubmission(context, OperationType.HTLC_CLAIM, 'hco', context.msbInstance.htlcClaim);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function handleHtlcRefund(context) {
|
|
133
|
+
return handleHtlcSubmission(context, OperationType.HTLC_REFUND, 'hro', context.msbInstance.htlcRefund);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function handleHtlc({ req, msbInstance, respond }) {
|
|
137
|
+
const lockId = req.params?.id;
|
|
138
|
+
if (!isValidTxHash(lockId)) return respond(400, { error: 'Invalid HTLC lock ID.' });
|
|
139
|
+
const confirmed = getConfirmedParameter(buildRequestUrl(req));
|
|
140
|
+
if (confirmed === null) {
|
|
141
|
+
return respond(400, { error: 'Parameter "confirmed" must be exactly "true" or "false"' });
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const htlc = await msbInstance.getHtlc(lockId, confirmed);
|
|
145
|
+
if (!htlc) return respond(404, { error: `HTLC ${lockId.toLowerCase()} not found.` });
|
|
146
|
+
respond(200, { htlc });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Error in handleHtlc:', error);
|
|
149
|
+
respond(500, { error: 'An error occurred processing the request.' });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
49
152
|
export async function handleTxv({ msbInstance, respond }) {
|
|
50
153
|
const txv = await msbInstance.getTxv();
|
|
51
154
|
respond(200, { txv });
|
package/rpc/routes/v1.js
CHANGED
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
handleFetchBulkTxPayloads,
|
|
12
12
|
handleTransactionExtendedDetails,
|
|
13
13
|
handleAccountDetails,
|
|
14
|
+
handleHtlcLock,
|
|
15
|
+
handleHtlcClaim,
|
|
16
|
+
handleHtlcRefund,
|
|
17
|
+
handleHtlc,
|
|
14
18
|
} from '../handlers.js';
|
|
15
19
|
|
|
16
20
|
export const v1Routes = [
|
|
@@ -22,6 +26,10 @@ export const v1Routes = [
|
|
|
22
26
|
{ method: 'POST', path: '/broadcast-transaction', handler: handleBroadcastTransaction },
|
|
23
27
|
{ method: 'GET', path: '/tx-hashes/', handler: handleTxHashes },
|
|
24
28
|
{ method: 'GET', path: '/unconfirmed-length', handler: handleUnconfirmedLength },
|
|
29
|
+
{ method: 'POST', path: '/htlc', exact: true, handler: handleHtlcLock },
|
|
30
|
+
{ method: 'POST', path: '/htlc/:id/claim', handler: handleHtlcClaim },
|
|
31
|
+
{ method: 'POST', path: '/htlc/:id/refund', handler: handleHtlcRefund },
|
|
32
|
+
{ method: 'GET', path: '/htlc/:id', handler: handleHtlc },
|
|
25
33
|
{ method: 'GET', path: '/tx', handler: handleTransactionDetails },
|
|
26
34
|
{ method: 'POST', path: '/tx-payloads-bulk', handler: handleFetchBulkTxPayloads },
|
|
27
35
|
{ method: 'GET', path: '/tx/details', handler: handleTransactionExtendedDetails },
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
2
|
+
import applyOperationsGenerated from './applyOperations.generated.cjs';
|
|
3
|
+
import consensusV1Generated from '../consensus/v1/consensusV1.generated.cjs';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
const { Operation, SetEpochOperation, ConsensusControlOperation } = applyOperationsGenerated.apply.operations;
|
|
6
|
+
const { EpochProofV1 } = consensusV1Generated.common.consensus;
|
|
7
|
+
|
|
8
|
+
// Options for converting protobuf messages to plain objects, ensuring that bytes are returned as Buffers and enums as numbers.
|
|
9
|
+
const APPLY_TO_OBJECT_OPTIONS = Object.freeze({
|
|
10
|
+
enums: Number,
|
|
11
|
+
longs: Number,
|
|
12
|
+
defaults: false,
|
|
13
|
+
arrays: true,
|
|
14
|
+
oneofs: false
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const normalizeDecodedApplyOperation = operation => {
|
|
18
|
+
const payload = operation.tro || operation.rao || operation.bdo || operation.txo || operation.hlo;
|
|
19
|
+
if (!payload) return operation;
|
|
20
|
+
|
|
21
|
+
payload.va ??= null;
|
|
22
|
+
payload.vn ??= null;
|
|
23
|
+
payload.vs ??= null;
|
|
24
|
+
|
|
25
|
+
return operation;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const encodeApplyOperation = (payload) => {
|
|
29
|
+
const error = Operation.verify(payload);
|
|
30
|
+
if (error) throw new Error(error);
|
|
31
|
+
return b4a.from(Operation.encode(payload).finish());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const decodeApplyOperation = (payload) => {
|
|
35
|
+
return normalizeDecodedApplyOperation(
|
|
36
|
+
Operation.toObject(
|
|
37
|
+
Operation.decode(payload),
|
|
38
|
+
APPLY_TO_OBJECT_OPTIONS
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Safely encodes an operation using the generated apply Operation encoder.
|
|
45
|
+
* If the encoding fails (e.g., due to an invalid payload), returns an empty Buffer.
|
|
46
|
+
*
|
|
47
|
+
* @param {*} payload - Any input that should conform to the `applyOperation` schema.
|
|
48
|
+
* @returns {Buffer} - Encoded Buffer if successful, otherwise an empty Buffer (`b4a.alloc(0)`).
|
|
49
|
+
*/
|
|
50
|
+
export const safeEncodeApplyOperation = (payload) => {
|
|
51
|
+
try {
|
|
52
|
+
const result = encodeApplyOperation(payload);
|
|
53
|
+
if (b4a.isBuffer(result)) return result
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.log("safeEncodeApplyOperation error:", error.message);
|
|
56
|
+
}
|
|
57
|
+
return b4a.alloc(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Safely decodes a Buffer into an `Operation` object using the generated apply Operation decoder.
|
|
62
|
+
* Returns `null` if decoding fails or the input is invalid.
|
|
63
|
+
*
|
|
64
|
+
* @param {Buffer} payload - A buffer containing encoded data.
|
|
65
|
+
* @returns {Object|null} - Decoded `applyOperation` object on success, or `null` on failure.
|
|
66
|
+
*/
|
|
67
|
+
export const safeDecodeApplyOperation = (payload) => {
|
|
68
|
+
try {
|
|
69
|
+
if (!b4a.isBuffer(payload)) return null;
|
|
70
|
+
return decodeApplyOperation(payload);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.log(error);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const unsafeDecodeApplyOperation= (payload) => {
|
|
78
|
+
return decodeApplyOperation(payload);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const unsafeEncodeApplyOperation = (payload) => {
|
|
82
|
+
return encodeApplyOperation(payload);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const normalizeIncomingMessage = (message) => {
|
|
86
|
+
if (!message) return null;
|
|
87
|
+
if (b4a.isBuffer(message)) {
|
|
88
|
+
return decodeApplyOperation(message);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (message.type === 'Buffer' && Array.isArray(message.data)) {
|
|
92
|
+
const buffer = b4a.from(message.data);
|
|
93
|
+
return decodeApplyOperation(buffer);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const validateEpochRecord = (payload) => {
|
|
100
|
+
if (!_.isPlainObject(payload)) {
|
|
101
|
+
throw new Error('Epoch record must be an object.');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const { sv, data } = payload;
|
|
105
|
+
if (!b4a.isBuffer(sv) || sv.length !== 1 || sv[0] === 0) {
|
|
106
|
+
throw new Error('Epoch record schema version must be a non-zero one-byte buffer.');
|
|
107
|
+
}
|
|
108
|
+
if (!b4a.isBuffer(data) || data.length === 0) {
|
|
109
|
+
throw new Error('Epoch record data must be a non-empty buffer.');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { sv, data };
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Encodes an epoch record using the SET_EPOCH payload wire format, not a full operation.
|
|
117
|
+
* The versioned data stays opaque; consensus validation belongs to apply.
|
|
118
|
+
*
|
|
119
|
+
* @param {{sv: Buffer, data: Buffer}} payload Epoch record.
|
|
120
|
+
* @returns {Buffer} Canonically encoded epoch record.
|
|
121
|
+
*/
|
|
122
|
+
export const encodeEpochRecord = (payload) => {
|
|
123
|
+
const epochRecord = validateEpochRecord(payload);
|
|
124
|
+
const error = SetEpochOperation.verify(epochRecord);
|
|
125
|
+
if (error) throw new Error(error);
|
|
126
|
+
return b4a.from(SetEpochOperation.encode(epochRecord).finish());
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Decodes an epoch record independently of the currently active consensus.
|
|
131
|
+
*
|
|
132
|
+
* @param {Buffer} payload Encoded epoch record.
|
|
133
|
+
* @returns {{sv: Buffer, data: Buffer}} Epoch record with opaque versioned data.
|
|
134
|
+
*/
|
|
135
|
+
export const decodeEpochRecord = (payload) => {
|
|
136
|
+
if (!b4a.isBuffer(payload)) {
|
|
137
|
+
throw new Error('Encoded epoch record must be a buffer.');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return validateEpochRecord(
|
|
141
|
+
SetEpochOperation.toObject(
|
|
142
|
+
SetEpochOperation.decode(payload),
|
|
143
|
+
APPLY_TO_OBJECT_OPTIONS
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {*} payload Epoch record to encode.
|
|
150
|
+
* @returns {Buffer} Encoded epoch record, or an empty buffer on failure.
|
|
151
|
+
*/
|
|
152
|
+
export const safeEncodeEpochRecord = (payload) => {
|
|
153
|
+
try {
|
|
154
|
+
return encodeEpochRecord(payload);
|
|
155
|
+
} catch {
|
|
156
|
+
return b4a.alloc(0);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param {*} payload Encoded epoch record.
|
|
162
|
+
* @returns {{sv: Buffer, data: Buffer}|null} Epoch record, or null on failure.
|
|
163
|
+
*/
|
|
164
|
+
export const safeDecodeEpochRecord = (payload) => {
|
|
165
|
+
try {
|
|
166
|
+
return decodeEpochRecord(payload);
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const validateEpochProofV1 = (payload) => {
|
|
173
|
+
if (!_.isPlainObject(payload)) {
|
|
174
|
+
throw new Error('EpochProofV1 payload must be an object.');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const { pd, app } = payload;
|
|
178
|
+
|
|
179
|
+
if (!b4a.isBuffer(pd) || pd.length === 0) {
|
|
180
|
+
throw new Error('EpochProofV1 pd must be a non-empty buffer.');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!Array.isArray(app)) {
|
|
184
|
+
throw new Error('EpochProofV1 app must be an array.');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (const [index, approval] of app.entries()) {
|
|
188
|
+
if (!b4a.isBuffer(approval) || approval.length === 0) {
|
|
189
|
+
throw new Error(`EpochProofV1 app ${index} must be a non-empty buffer.`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { pd, app };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Encodes an EpochProofV1.
|
|
198
|
+
*
|
|
199
|
+
* @param {{pd: Buffer, app: Buffer[]}} payload - Epoch proof payload.
|
|
200
|
+
* @returns {Buffer} Encoded EpochProofV1.
|
|
201
|
+
*/
|
|
202
|
+
export const encodeEpochProofV1 = (payload) => {
|
|
203
|
+
const epochProof = validateEpochProofV1(payload);
|
|
204
|
+
const error = EpochProofV1.verify(epochProof);
|
|
205
|
+
if (error) throw new Error(error);
|
|
206
|
+
return b4a.from(EpochProofV1.encode(epochProof).finish());
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Decodes an EpochProofV1.
|
|
211
|
+
*
|
|
212
|
+
* @param {Buffer} payload - Encoded EpochProofV1 buffer.
|
|
213
|
+
* @returns {{pd: Buffer, app: Buffer[]}} Decoded EpochProofV1.
|
|
214
|
+
*/
|
|
215
|
+
export const decodeEpochProofV1 = (payload) => {
|
|
216
|
+
return validateEpochProofV1(
|
|
217
|
+
EpochProofV1.toObject(
|
|
218
|
+
EpochProofV1.decode(payload),
|
|
219
|
+
APPLY_TO_OBJECT_OPTIONS
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Safely encodes an EpochProofV1. Returns an empty buffer when the payload is invalid.
|
|
226
|
+
*
|
|
227
|
+
* @param {*} payload - Input expected to match EpochProofV1.
|
|
228
|
+
* @returns {Buffer} Encoded EpochProofV1 or an empty buffer.
|
|
229
|
+
*/
|
|
230
|
+
export const safeEncodeEpochProofV1 = (payload) => {
|
|
231
|
+
try {
|
|
232
|
+
return encodeEpochProofV1(payload);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.log("safeEncodeEpochProofV1 error:", error.message);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return b4a.alloc(0);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Safely decodes an EpochProofV1. Returns null when the input is invalid.
|
|
242
|
+
*
|
|
243
|
+
* @param {*} payload - Encoded EpochProofV1 buffer.
|
|
244
|
+
* @returns {{pd: Buffer, app: Buffer[]}|null} Decoded EpochProofV1 or null.
|
|
245
|
+
*/
|
|
246
|
+
export const safeDecodeEpochProofV1 = (payload) => {
|
|
247
|
+
try {
|
|
248
|
+
if (!b4a.isBuffer(payload)) return null;
|
|
249
|
+
return decodeEpochProofV1(payload);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.log("safeDecodeEpochProofV1 error:", error.message);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const getValidatedConsensusConfigPayload = (payload) => {
|
|
258
|
+
if (!_.isPlainObject(payload)) {
|
|
259
|
+
throw new Error('ConsensusConfig payload must be an object.');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const { sv, cd} = payload;
|
|
263
|
+
|
|
264
|
+
if (!b4a.isBuffer(sv) || sv.length !== 1) {
|
|
265
|
+
throw new Error('Schema version must be a one-byte buffer.');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!b4a.isBuffer(cd)) {
|
|
269
|
+
throw new Error('ConsensusConfig configData must be a buffer.');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return { sv, cd };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Encodes a ConsensusConfig using the ConsensusControlOperation wire format.
|
|
277
|
+
*
|
|
278
|
+
* @param {{sv: Buffer, cd: Buffer}} payload - Consensus config payload.
|
|
279
|
+
* @returns {Buffer} Encoded ConsensusConfig.
|
|
280
|
+
*/
|
|
281
|
+
export const encodeConsensusConfig = (payload) => {
|
|
282
|
+
const consensusConfigPayload = getValidatedConsensusConfigPayload(payload);
|
|
283
|
+
const consensusControlPayload = { cc: consensusConfigPayload };
|
|
284
|
+
const error = ConsensusControlOperation.verify(consensusControlPayload);
|
|
285
|
+
if (error) throw new Error(error);
|
|
286
|
+
return b4a.from(ConsensusControlOperation.encode(consensusControlPayload).finish());
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Decodes a ConsensusConfig encoded with the ConsensusControlOperation wire format.
|
|
291
|
+
*
|
|
292
|
+
* @param {Buffer} payload - Encoded ConsensusConfig buffer.
|
|
293
|
+
* @returns {{sv: Buffer, cd: Buffer}} Decoded ConsensusConfig.
|
|
294
|
+
*/
|
|
295
|
+
export const decodeConsensusConfig = (payload) => {
|
|
296
|
+
if (!b4a.isBuffer(payload)) {
|
|
297
|
+
throw new Error('Encoded ConsensusConfig must be a buffer.');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const consensusControlPayload = ConsensusControlOperation.toObject(
|
|
301
|
+
ConsensusControlOperation.decode(payload),
|
|
302
|
+
APPLY_TO_OBJECT_OPTIONS
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
return getValidatedConsensusConfigPayload(consensusControlPayload.cc);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Safely encodes a ConsensusConfig. Returns an empty buffer when the payload is invalid.
|
|
310
|
+
*
|
|
311
|
+
* @param {*} payload - Input expected to match ConsensusConfig.
|
|
312
|
+
* @returns {Buffer} Encoded ConsensusConfig or an empty buffer.
|
|
313
|
+
*/
|
|
314
|
+
export const safeEncodeConsensusConfig = (payload) => {
|
|
315
|
+
try {
|
|
316
|
+
return encodeConsensusConfig(payload);
|
|
317
|
+
} catch {
|
|
318
|
+
return b4a.alloc(0);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Safely decodes a ConsensusConfig. Returns null when the input is invalid.
|
|
324
|
+
*
|
|
325
|
+
* @param {*} payload - Encoded ConsensusConfig buffer.
|
|
326
|
+
* @returns {{sv: Buffer, cd: Buffer}|null} Decoded ConsensusConfig or null.
|
|
327
|
+
*/
|
|
328
|
+
export const safeDecodeConsensusConfig = (payload) => {
|
|
329
|
+
try {
|
|
330
|
+
return decodeConsensusConfig(payload);
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|