trac-msb 0.2.26 → 0.3.0-beta.2
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 +121 -0
- package/rpc/routes/v1.js +12 -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 +1803 -159
- 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 +280 -67
- 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/src/index.js
CHANGED
|
@@ -2,40 +2,55 @@ import ReadyResource from "ready-resource";
|
|
|
2
2
|
import Corestore from "corestore";
|
|
3
3
|
import tracCryptoApi from "trac-crypto-api";
|
|
4
4
|
import b4a from "b4a";
|
|
5
|
+
import _ from "lodash";
|
|
5
6
|
import { sleep, isHexString } from "./utils/helpers.js";
|
|
6
7
|
import { applyStateMessageFactory } from "./messages/state/applyStateMessageFactory.js";
|
|
7
|
-
import { isAddressValid } from "./core/state/utils/address.js";
|
|
8
|
+
import { isAddressValid, bufferToAddress } from "./core/state/utils/address.js";
|
|
8
9
|
import Network from "./core/network/Network.js";
|
|
9
|
-
import Check from "./utils/check.js";
|
|
10
10
|
import State from "./core/state/State.js";
|
|
11
11
|
import {
|
|
12
|
-
EventType,
|
|
13
12
|
WHITELIST_SLEEP_INTERVAL,
|
|
14
13
|
BOOTSTRAP_HEXSTRING_LENGTH,
|
|
15
|
-
CustomEventType,
|
|
16
14
|
BALANCE_MIGRATION_SLEEP_INTERVAL,
|
|
17
15
|
WHITELIST_MIGRATION_DIR,
|
|
18
|
-
OperationType
|
|
16
|
+
OperationType,
|
|
17
|
+
EntryType,
|
|
18
|
+
HTLC_LOCK_ID_BYTE_LENGTH,
|
|
19
|
+
ConsensusVersion,
|
|
20
|
+
MAX_VDF_DIFFICULTY,
|
|
21
|
+
VDF_PROOF_BYTE_LENGTHS
|
|
19
22
|
} from "./utils/constants.js";
|
|
20
23
|
import { decimalStringToBigInt, bigIntTo16ByteBuffer, bufferToBigInt, bigIntToDecimalString } from "./utils/amountSerialization.js"
|
|
21
24
|
import {
|
|
22
25
|
normalizeDecodedPayloadForJson,
|
|
23
26
|
normalizeTransactionOperation,
|
|
24
|
-
normalizeTransferOperation
|
|
27
|
+
normalizeTransferOperation,
|
|
28
|
+
normalizeMessageByOperationType
|
|
25
29
|
} from "./utils/normalizers.js";
|
|
30
|
+
import { operationToPayload } from "./utils/applyOperations.js";
|
|
31
|
+
import PartialHtlcValidator from "./core/network/protocols/validators/shared/validators/PartialHtlcValidator.js";
|
|
32
|
+
import { V1ProtocolError } from "./core/network/protocols/validators/v1/V1ProtocolError.js";
|
|
33
|
+
import * as escrowEntryUtils from "./core/state/utils/escrowEntry.js";
|
|
26
34
|
import fileUtils from './utils/fileUtils.js';
|
|
27
35
|
import migrationUtils from './utils/migrationUtils.js';
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
import {
|
|
37
|
+
encodeApplyOperation,
|
|
38
|
+
encodeConsensusConfig,
|
|
39
|
+
safeDecodeApplyOperation,
|
|
40
|
+
safeEncodeApplyOperation,
|
|
41
|
+
decodeEpochRecord
|
|
42
|
+
} from "./codecs/apply/applyOperationCodec.js";
|
|
43
|
+
import { encodeVdfConfig } from "./codecs/consensus/v1/vdfConfigCodec.js";
|
|
44
|
+
import PartialTransactionValidator from "./core/network/protocols/validators/shared/validators/PartialTransactionValidator.js";
|
|
45
|
+
import PartialTransferValidator from "./core/network/protocols/validators/shared/validators/PartialTransferValidator.js";
|
|
31
46
|
import { BroadcastError, ValidationError } from "./utils/errors.js";
|
|
47
|
+
import { uint8ToBuffer, uint16ToBuffer, uint32ToBuffer } from "./utils/buffer.js";
|
|
32
48
|
|
|
33
49
|
export class MainSettlementBus extends ReadyResource {
|
|
34
50
|
#store;
|
|
35
51
|
#wallet;
|
|
36
52
|
#network;
|
|
37
53
|
#state;
|
|
38
|
-
#isClosing = false;
|
|
39
54
|
#config
|
|
40
55
|
|
|
41
56
|
/**
|
|
@@ -47,7 +62,6 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
47
62
|
this.#config = config
|
|
48
63
|
this.#wallet = wallet;
|
|
49
64
|
this.#store = new Corestore(this.#config.storesFullPath);
|
|
50
|
-
this.check = new Check(this.#config);
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
get config() {
|
|
@@ -71,27 +85,18 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
71
85
|
|
|
72
86
|
async _open() {
|
|
73
87
|
this.#state = new State(this.#store, this.#wallet, this.#config);
|
|
74
|
-
this.#network = new Network(this.#state, this.#config, this.#wallet
|
|
88
|
+
this.#network = new Network(this.#state, this.#store, this.#config, this.#wallet);
|
|
75
89
|
|
|
76
90
|
await this.#state.ready();
|
|
77
91
|
await this.#network.ready();
|
|
78
|
-
await this.#stateEventsListener();
|
|
79
92
|
|
|
80
93
|
if (this.#wallet) {
|
|
81
94
|
this.#printWalletInfo();
|
|
82
95
|
}
|
|
83
96
|
|
|
84
|
-
await this.#network.replicate(
|
|
85
|
-
this.#state,
|
|
86
|
-
this.#store,
|
|
87
|
-
this.#wallet,
|
|
88
|
-
);
|
|
89
|
-
|
|
90
97
|
const adminEntry = await this.#state.getAdminEntry();
|
|
91
98
|
await this.#setUpRoleAutomatically(adminEntry);
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
100
|
console.log(`isIndexer: ${this.#state.isIndexer()}`);
|
|
96
101
|
console.log(`isWriter: ${this.#state.isWritable()}`);
|
|
97
102
|
console.log("MSB Unsigned Length:", this.#state.getUnsignedLength());
|
|
@@ -105,7 +110,6 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
105
110
|
async _close() {
|
|
106
111
|
console.log("Closing everything gracefully... This may take a moment.");
|
|
107
112
|
|
|
108
|
-
this.#isClosing = true;
|
|
109
113
|
await this.#network.close();
|
|
110
114
|
|
|
111
115
|
await sleep(100);
|
|
@@ -129,6 +133,58 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
129
133
|
return await this.#network.validatorMessageOrchestrator.send(partialTransactionPayload);
|
|
130
134
|
}
|
|
131
135
|
|
|
136
|
+
async htlcLock(payload) {
|
|
137
|
+
if (payload?.type !== OperationType.HTLC_LOCK) {
|
|
138
|
+
throw new ValidationError('HTLC lock payload is required.');
|
|
139
|
+
}
|
|
140
|
+
return this.broadcastTransaction(payload);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async htlcClaim(payload) {
|
|
144
|
+
if (payload?.type !== OperationType.HTLC_CLAIM) {
|
|
145
|
+
throw new ValidationError('HTLC claim payload is required.');
|
|
146
|
+
}
|
|
147
|
+
return this.broadcastTransaction(payload);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async htlcRefund(payload) {
|
|
151
|
+
if (payload?.type !== OperationType.HTLC_REFUND) {
|
|
152
|
+
throw new ValidationError('HTLC refund payload is required.');
|
|
153
|
+
}
|
|
154
|
+
return this.broadcastTransaction(payload);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getHtlc(lockId, confirmed = true) {
|
|
158
|
+
if (typeof lockId !== 'string' || lockId.length !== HTLC_LOCK_ID_BYTE_LENGTH * 2 || !isHexString(lockId)) {
|
|
159
|
+
throw new ValidationError('Invalid HTLC lock ID.');
|
|
160
|
+
}
|
|
161
|
+
const normalizedId = lockId.toLowerCase();
|
|
162
|
+
const key = EntryType.HTLC_ESCROW + normalizedId;
|
|
163
|
+
const entry = confirmed ? await this.#state.getSigned(key) : await this.#state.get(key);
|
|
164
|
+
if (!entry) return null;
|
|
165
|
+
|
|
166
|
+
const escrow = escrowEntryUtils.decode(entry, this.#config.addressPrefix);
|
|
167
|
+
if (!escrow || b4a.toString(escrow.lockId, 'hex') !== normalizedId) {
|
|
168
|
+
throw new Error('Stored HTLC escrow is invalid.');
|
|
169
|
+
}
|
|
170
|
+
const address = value => value ? bufferToAddress(value, this.#config.addressPrefix) : null;
|
|
171
|
+
const hex = value => value ? b4a.toString(value, 'hex') : null;
|
|
172
|
+
return {
|
|
173
|
+
lockId: normalizedId,
|
|
174
|
+
status: Object.keys(escrowEntryUtils.Status).find(name => escrowEntryUtils.Status[name] === escrow.status),
|
|
175
|
+
lockerAddress: address(escrow.lockerAddress),
|
|
176
|
+
claimRecipientAddress: address(escrow.claimRecipientAddress),
|
|
177
|
+
refundRecipientAddress: address(escrow.refundRecipientAddress),
|
|
178
|
+
amount: bufferToBigInt(escrow.amount).toString(),
|
|
179
|
+
additionalFeeAmount: bufferToBigInt(escrow.additionalFeeAmount).toString(),
|
|
180
|
+
additionalFeeRecipientAddress: address(escrow.additionalFeeRecipientAddress),
|
|
181
|
+
hashLock: hex(escrow.hashLock),
|
|
182
|
+
refundEpoch: escrow.refundEpoch.readBigUInt64BE(0).toString(),
|
|
183
|
+
metadata: hex(escrow.metadata),
|
|
184
|
+
preimage: hex(escrow.preimage),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
132
188
|
async validateTransfer(payload) {
|
|
133
189
|
const partialTransferValidator = new PartialTransferValidator(this.#state, null, this.#config);
|
|
134
190
|
return await partialTransferValidator.validate(payload);
|
|
@@ -140,6 +196,11 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
140
196
|
return await partialTransactionValidator.validate(payload);
|
|
141
197
|
}
|
|
142
198
|
|
|
199
|
+
async validateHtlc(payload) {
|
|
200
|
+
const validator = new PartialHtlcValidator(this.#state, null, this.#config);
|
|
201
|
+
return validator.validate(payload);
|
|
202
|
+
}
|
|
203
|
+
|
|
143
204
|
async broadcastTransaction(payload) {
|
|
144
205
|
if (!payload) {
|
|
145
206
|
throw new ValidationError("Transaction payload is required for broadcasting.");
|
|
@@ -161,6 +222,21 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
161
222
|
throw new ValidationError("Invalid transaction payload.");
|
|
162
223
|
}
|
|
163
224
|
hash = b4a.toString(normalizedPayload.txo.tx, "hex");
|
|
225
|
+
} else if ([OperationType.HTLC_LOCK, OperationType.HTLC_CLAIM, OperationType.HTLC_REFUND].includes(payload.type)) {
|
|
226
|
+
try {
|
|
227
|
+
normalizedPayload = normalizeMessageByOperationType(payload, this.#config);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
throw new ValidationError(error.message || 'Invalid HTLC payload.');
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
await this.validateHtlc(normalizedPayload);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (error instanceof V1ProtocolError) throw new ValidationError(error.message);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
hash = b4a.toString(normalizedPayload[operationToPayload(payload.type)].tx, 'hex');
|
|
238
|
+
} else {
|
|
239
|
+
throw new ValidationError('Unsupported transaction operation.');
|
|
164
240
|
}
|
|
165
241
|
|
|
166
242
|
const success = await this.broadcastPartialTransaction(payload);
|
|
@@ -202,37 +278,6 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
202
278
|
return nodeEntry?.isWhitelisted && !this.isAdmin(adminEntry);
|
|
203
279
|
}
|
|
204
280
|
|
|
205
|
-
async #stateEventsListener() {
|
|
206
|
-
this.#state.on(CustomEventType.IS_INDEXER, (publicKey) => {
|
|
207
|
-
if (this.#isClosing) return;
|
|
208
|
-
this.#network.disconnectValidatorPeer(publicKey, 'peer promoted to indexer');
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
this.#state.on(CustomEventType.UNWRITABLE, (publicKey) => {
|
|
212
|
-
if (this.#isClosing) return;
|
|
213
|
-
this.#network.disconnectValidatorPeer(publicKey, 'peer became unwritable');
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
this.#state.base.on(EventType.IS_INDEXER, () => {
|
|
217
|
-
console.log("Current node is an indexer");
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
this.#state.base.on(EventType.IS_NON_INDEXER, async () => {
|
|
221
|
-
// Prevent further actions if closing is in progress
|
|
222
|
-
// The reason is that getNodeEntry is async and may cause issues if we will access state after closing
|
|
223
|
-
if (this.#isClosing) return;
|
|
224
|
-
console.log("Current node is not an indexer anymore");
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
this.#state.base.on(EventType.WRITABLE, async () => {
|
|
228
|
-
console.log("Current node is writable");
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
this.#state.base.on(EventType.UNWRITABLE, async () => {
|
|
232
|
-
console.log("Current node is unwritable");
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
281
|
async getConfirmedTxInfo(txHash) {
|
|
237
282
|
const payload = await this.#state.getSigned(txHash);
|
|
238
283
|
if (!payload) return null
|
|
@@ -257,7 +302,7 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
257
302
|
return { payload, decoded }
|
|
258
303
|
}
|
|
259
304
|
|
|
260
|
-
handleGetFee() {
|
|
305
|
+
handleGetFee() {
|
|
261
306
|
const fee = this.#state.getFee();
|
|
262
307
|
return bufferToBigInt(fee);
|
|
263
308
|
}
|
|
@@ -297,7 +342,7 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
297
342
|
let dagSystem = await this.#state.base.system.core.treeHash();
|
|
298
343
|
let lengthdagSystem = this.#state.base.system.core.length;
|
|
299
344
|
const wl = await this.#state.getWriterLength();
|
|
300
|
-
|
|
345
|
+
|
|
301
346
|
console.log("---------- node & network stats ----------");
|
|
302
347
|
console.log("wallet.publicKey:", this.#wallet?.publicKey?.toString("hex") ?? "unset");
|
|
303
348
|
console.log("wallet.address:", this.#wallet?.address ?? "unset");
|
|
@@ -332,6 +377,9 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
332
377
|
console.log("- /add_indexer <address>: Change a role of the selected writer node to indexer role. Charges a fee.");
|
|
333
378
|
console.log("- /remove_indexer <address>: Change a role of the selected indexer node to default role. Charges a fee.");
|
|
334
379
|
console.log("- /ban_writer <address>: Demote a whitelisted writer to default role and remove it from the whitelist. Charges a fee.");
|
|
380
|
+
console.log("- /init_genesis: Initialize genesis epoch.");
|
|
381
|
+
console.log("- /set_consensus_config: Set new configuration data for a specific consensus schema version from JSON.");
|
|
382
|
+
console.log("- /set_vdf_params: Update VDF difficulty and discriminant bit size.");
|
|
335
383
|
}
|
|
336
384
|
console.log("Available commands:");
|
|
337
385
|
console.log("- /add_writer: Add yourself as a validator to this MSB once whitelisted. Requires a fee + 10x the fee as a stake in $TNK.");
|
|
@@ -378,7 +426,16 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
378
426
|
|
|
379
427
|
async fetchBulkTxPayloads(hashes) {
|
|
380
428
|
const response = { results: [], missing: [] };
|
|
381
|
-
const results = await Promise.all(hashes.map((hash) =>
|
|
429
|
+
const results = await Promise.all(hashes.map(async (hash) => {
|
|
430
|
+
const transaction = await this.getConfirmedTxInfo(hash);
|
|
431
|
+
if (transaction) return transaction.decoded;
|
|
432
|
+
|
|
433
|
+
const epochRecord = await this.#state.getEpochProof(hash);
|
|
434
|
+
if (epochRecord === null) return null;
|
|
435
|
+
|
|
436
|
+
// This hash identifies the stored epoch record, not an original transaction.
|
|
437
|
+
return { type: OperationType.SET_EPOCH, seo: decodeEpochRecord(epochRecord) };
|
|
438
|
+
}));
|
|
382
439
|
|
|
383
440
|
results.forEach((result, index) => {
|
|
384
441
|
const hash = hashes[index];
|
|
@@ -387,7 +444,7 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
387
444
|
return;
|
|
388
445
|
}
|
|
389
446
|
|
|
390
|
-
const decodedResult = normalizeDecodedPayloadForJson(result
|
|
447
|
+
const decodedResult = normalizeDecodedPayloadForJson(result, this.#config);
|
|
391
448
|
response.results.push({ hash, payload: decodedResult });
|
|
392
449
|
});
|
|
393
450
|
|
|
@@ -746,45 +803,45 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
746
803
|
}
|
|
747
804
|
}
|
|
748
805
|
|
|
749
|
-
async banValidator(
|
|
806
|
+
async banValidator(addressToBan) {
|
|
750
807
|
if (!this.#config.enableWallet) {
|
|
751
808
|
throw new Error(
|
|
752
|
-
`Can not ban writer with address: ${
|
|
809
|
+
`Can not ban writer with address: ${addressToBan} - wallet is not enabled.`
|
|
753
810
|
);
|
|
754
811
|
}
|
|
755
812
|
const adminEntry = await this.#state.getAdminEntry();
|
|
756
813
|
|
|
757
814
|
if (!adminEntry) {
|
|
758
815
|
throw new Error(
|
|
759
|
-
`Can not ban writer with address: ${
|
|
816
|
+
`Can not ban writer with address: ${addressToBan} - admin entry has not been initialized.`
|
|
760
817
|
);
|
|
761
818
|
}
|
|
762
819
|
|
|
763
|
-
if (!isAddressValid(
|
|
820
|
+
if (!isAddressValid(addressToBan, this.#config.addressPrefix)) {
|
|
764
821
|
throw new Error(
|
|
765
|
-
`Can not ban writer with address: ${
|
|
822
|
+
`Can not ban writer with address: ${addressToBan} - invalid address.`
|
|
766
823
|
);
|
|
767
824
|
}
|
|
768
825
|
|
|
769
826
|
if (!this.isAdmin(adminEntry)) {
|
|
770
827
|
throw new Error(
|
|
771
|
-
`Can not ban writer with address: ${
|
|
828
|
+
`Can not ban writer with address: ${addressToBan} - You are not an admin.`
|
|
772
829
|
);
|
|
773
830
|
}
|
|
774
831
|
|
|
775
|
-
const isWhitelisted = await this.#state.isAddressWhitelisted(
|
|
776
|
-
const nodeEntry = await this.#state.getNodeEntry(
|
|
832
|
+
const isWhitelisted = await this.#state.isAddressWhitelisted(addressToBan);
|
|
833
|
+
const nodeEntry = await this.#state.getNodeEntry(addressToBan);
|
|
777
834
|
|
|
778
835
|
if (!isWhitelisted || null === nodeEntry || nodeEntry.isIndexer === true) {
|
|
779
836
|
throw new Error(
|
|
780
|
-
`Can not ban writer with address: ${
|
|
837
|
+
`Can not ban writer with address: ${addressToBan} - node is not whitelisted or is an indexer.`
|
|
781
838
|
);
|
|
782
839
|
}
|
|
783
840
|
const txValidity = await this.#state.getIndexerSequenceState();
|
|
784
841
|
const assembledBanValidatorMessage = await applyStateMessageFactory(this.#wallet, this.#config)
|
|
785
842
|
.buildCompleteBanWriterMessage(
|
|
786
843
|
this.#wallet.address,
|
|
787
|
-
|
|
844
|
+
addressToBan,
|
|
788
845
|
txValidity,
|
|
789
846
|
)
|
|
790
847
|
const encodedPayload = safeEncodeApplyOperation(assembledBanValidatorMessage)
|
|
@@ -1100,6 +1157,162 @@ export class MainSettlementBus extends ReadyResource {
|
|
|
1100
1157
|
const encodedPayload = safeEncodeApplyOperation(payload);
|
|
1101
1158
|
await this.#state.append(encodedPayload);
|
|
1102
1159
|
}
|
|
1160
|
+
|
|
1161
|
+
async handleEpochGenesisInitialization(consensusConfig) {
|
|
1162
|
+
if (!this.#config.enableWallet) {
|
|
1163
|
+
throw new Error("Can not initialize genesis epoch - wallet is not enabled.");
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
const adminEntry = await this.#state.getAdminEntry();
|
|
1167
|
+
|
|
1168
|
+
if (!adminEntry) {
|
|
1169
|
+
throw new Error(
|
|
1170
|
+
"Can not initialize genesis epoch - admin has not been initialized."
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
if (!this.#wallet) {
|
|
1174
|
+
throw new Error("Can not initialize genesis epoch - wallet is not initialized.");
|
|
1175
|
+
}
|
|
1176
|
+
if (!this.isAdmin(adminEntry)) {
|
|
1177
|
+
throw new Error("Can not initialize genesis epoch - you are not the admin.");
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
const existingConsensusConfig = await this.#state.getSignedConsensusConfig();
|
|
1181
|
+
if (existingConsensusConfig) {
|
|
1182
|
+
throw new Error("Can not initialize genesis epoch - consensus config already exists.");
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const encodedConsensusConfig = this.#validateAndEncodeConsensusConfig(consensusConfig);
|
|
1186
|
+
|
|
1187
|
+
const txValidity = await this.#state.getIndexerSequenceState();
|
|
1188
|
+
const payload = await applyStateMessageFactory(this.#wallet, this.#config)
|
|
1189
|
+
.buildCompleteSetGenesisEpochMessage(
|
|
1190
|
+
this.#wallet.address,
|
|
1191
|
+
txValidity,
|
|
1192
|
+
encodedConsensusConfig,
|
|
1193
|
+
)
|
|
1194
|
+
const encodedPayload = encodeApplyOperation(payload);
|
|
1195
|
+
await this.#state.append(encodedPayload);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
async handleSetConsensusConfig(consensusConfig) {
|
|
1199
|
+
if (!this.#config.enableWallet) {
|
|
1200
|
+
throw new Error("Can not set consensus config - wallet is not enabled.");
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
const adminEntry = await this.#state.getAdminEntry();
|
|
1204
|
+
|
|
1205
|
+
if (!adminEntry) {
|
|
1206
|
+
throw new Error("Can not set consensus config - admin has not been initialized.");
|
|
1207
|
+
}
|
|
1208
|
+
if (!this.#wallet) {
|
|
1209
|
+
throw new Error("Can not set consensus config - wallet is not initialized.");
|
|
1210
|
+
}
|
|
1211
|
+
if (!this.isAdmin(adminEntry)) {
|
|
1212
|
+
throw new Error("Can not set consensus config - you are not the admin.");
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
const encodedConsensusConfig = this.#validateAndEncodeConsensusConfig(consensusConfig);
|
|
1216
|
+
|
|
1217
|
+
const txValidity = await this.#state.getIndexerSequenceState();
|
|
1218
|
+
const payload = await applyStateMessageFactory(this.#wallet, this.#config)
|
|
1219
|
+
.buildCompleteSetConsensusConfigMessage(
|
|
1220
|
+
this.#wallet.address,
|
|
1221
|
+
txValidity,
|
|
1222
|
+
encodedConsensusConfig
|
|
1223
|
+
);
|
|
1224
|
+
const encodedPayload = encodeApplyOperation(payload);
|
|
1225
|
+
await this.#state.append(encodedPayload);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
#validateAndEncodeConsensusConfig(consensusConfig) {
|
|
1229
|
+
if (!this.#hasExactKeys(consensusConfig, ["schemaVersion", "configData"])) {
|
|
1230
|
+
throw new Error(
|
|
1231
|
+
"Consensus config must contain only schemaVersion and configData."
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
const { schemaVersion, configData } = consensusConfig;
|
|
1236
|
+
if (
|
|
1237
|
+
!Number.isInteger(schemaVersion) ||
|
|
1238
|
+
schemaVersion <= 0 ||
|
|
1239
|
+
schemaVersion > 0xFF
|
|
1240
|
+
) {
|
|
1241
|
+
throw new Error(
|
|
1242
|
+
"Consensus config schemaVersion must be an integer from 1 to 255."
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
let encodedConfigData;
|
|
1247
|
+
switch (schemaVersion) {
|
|
1248
|
+
case ConsensusVersion.VDF_V1:
|
|
1249
|
+
encodedConfigData = this.#encodeVdfV1ConfigData(configData);
|
|
1250
|
+
break;
|
|
1251
|
+
default:
|
|
1252
|
+
throw new Error(
|
|
1253
|
+
`Unsupported consensus config schema version: ${schemaVersion}.`
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
return encodeConsensusConfig({
|
|
1258
|
+
sv: uint8ToBuffer(schemaVersion),
|
|
1259
|
+
cd: encodedConfigData
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// TODO: REFACTOR - In the future, this function should be extracted into another module.
|
|
1264
|
+
#encodeVdfV1ConfigData(configData) {
|
|
1265
|
+
if (!this.#hasExactKeys(configData, ["difficulty", "discriminantBitSize"])) {
|
|
1266
|
+
throw new Error(
|
|
1267
|
+
"Consensus config schema version 1 configData must contain only difficulty and discriminantBitSize."
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
if (
|
|
1272
|
+
!Number.isInteger(configData.difficulty) ||
|
|
1273
|
+
configData.difficulty <= 0 ||
|
|
1274
|
+
configData.difficulty > MAX_VDF_DIFFICULTY
|
|
1275
|
+
) {
|
|
1276
|
+
throw new Error(
|
|
1277
|
+
"VDF difficulty must be a positive unsigned 32-bit integer."
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
if (
|
|
1282
|
+
!Number.isInteger(configData.discriminantBitSize) ||
|
|
1283
|
+
!Object.hasOwn(VDF_PROOF_BYTE_LENGTHS, configData.discriminantBitSize)
|
|
1284
|
+
) {
|
|
1285
|
+
throw new Error(
|
|
1286
|
+
"VDF discriminant bit size must be one of: 1024, 2048, 4096."
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
return encodeVdfConfig({
|
|
1291
|
+
difficulty: uint32ToBuffer(configData.difficulty),
|
|
1292
|
+
discriminantBitSize: uint16ToBuffer(configData.discriminantBitSize)
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
#hasExactKeys(value, expectedKeys) {
|
|
1297
|
+
return (
|
|
1298
|
+
_.isPlainObject(value) &&
|
|
1299
|
+
Object.keys(value).length === expectedKeys.length &&
|
|
1300
|
+
expectedKeys.every(key => Object.hasOwn(value, key))
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
async getCurrentEpoch(confirmed = true) {
|
|
1305
|
+
const epoch = confirmed
|
|
1306
|
+
? await this.#state.getCurrentEpoch()
|
|
1307
|
+
: await this.#state.getCurrentEpochUnsigned();
|
|
1308
|
+
return epoch === null ? null : epoch.toString();
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
async getConsensusConfig(confirmed = true) {
|
|
1312
|
+
return confirmed
|
|
1313
|
+
? this.#state.getSignedConsensusConfig()
|
|
1314
|
+
: this.#state.getUnsignedConsensusConfig();
|
|
1315
|
+
}
|
|
1103
1316
|
}
|
|
1104
1317
|
|
|
1105
1318
|
export default MainSettlementBus;
|