@mainsail/consensus 0.0.1-evm.5 → 0.0.1-evm.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/distribution/aggregator.d.ts +3 -4
  2. package/distribution/aggregator.d.ts.map +1 -1
  3. package/distribution/aggregator.js +11 -9
  4. package/distribution/aggregator.js.map +1 -1
  5. package/distribution/bootstrapper.d.ts +1 -1
  6. package/distribution/bootstrapper.d.ts.map +1 -1
  7. package/distribution/bootstrapper.js +15 -16
  8. package/distribution/bootstrapper.js.map +1 -1
  9. package/distribution/commit-state.d.ts +2 -3
  10. package/distribution/commit-state.d.ts.map +1 -1
  11. package/distribution/commit-state.js +21 -41
  12. package/distribution/commit-state.js.map +1 -1
  13. package/distribution/consensus.d.ts +10 -9
  14. package/distribution/consensus.d.ts.map +1 -1
  15. package/distribution/consensus.js +274 -224
  16. package/distribution/consensus.js.map +1 -1
  17. package/distribution/index.d.ts +1 -5
  18. package/distribution/index.d.ts.map +1 -1
  19. package/distribution/index.js +1 -30
  20. package/distribution/index.js.map +1 -1
  21. package/distribution/processors/abstract-processor.d.ts +5 -5
  22. package/distribution/processors/abstract-processor.d.ts.map +1 -1
  23. package/distribution/processors/abstract-processor.js +11 -7
  24. package/distribution/processors/abstract-processor.js.map +1 -1
  25. package/distribution/processors/commit-processor.d.ts +1 -2
  26. package/distribution/processors/commit-processor.d.ts.map +1 -1
  27. package/distribution/processors/commit-processor.js +29 -46
  28. package/distribution/processors/commit-processor.js.map +1 -1
  29. package/distribution/processors/index.d.ts +1 -2
  30. package/distribution/processors/index.d.ts.map +1 -1
  31. package/distribution/processors/index.js +1 -2
  32. package/distribution/processors/index.js.map +1 -1
  33. package/distribution/processors/message-processor.d.ts +13 -0
  34. package/distribution/processors/message-processor.d.ts.map +1 -0
  35. package/distribution/processors/message-processor.js +117 -0
  36. package/distribution/processors/message-processor.js.map +1 -0
  37. package/distribution/processors/proposal-processor.d.ts +3 -2
  38. package/distribution/processors/proposal-processor.d.ts.map +1 -1
  39. package/distribution/processors/proposal-processor.js +46 -41
  40. package/distribution/processors/proposal-processor.js.map +1 -1
  41. package/distribution/round-state-repository.d.ts +2 -2
  42. package/distribution/round-state-repository.d.ts.map +1 -1
  43. package/distribution/round-state-repository.js +13 -23
  44. package/distribution/round-state-repository.js.map +1 -1
  45. package/distribution/round-state.d.ts +14 -10
  46. package/distribution/round-state.d.ts.map +1 -1
  47. package/distribution/round-state.js +173 -153
  48. package/distribution/round-state.js.map +1 -1
  49. package/distribution/scheduler.d.ts +1 -1
  50. package/distribution/scheduler.d.ts.map +1 -1
  51. package/distribution/scheduler.js +49 -65
  52. package/distribution/scheduler.js.map +1 -1
  53. package/distribution/service-provider.d.ts +6 -0
  54. package/distribution/service-provider.d.ts.map +1 -0
  55. package/distribution/service-provider.js +42 -0
  56. package/distribution/service-provider.js.map +1 -0
  57. package/package.json +14 -11
  58. package/distribution/processors/precommit-processor.d.ts +0 -12
  59. package/distribution/processors/precommit-processor.d.ts.map +0 -1
  60. package/distribution/processors/precommit-processor.js +0 -77
  61. package/distribution/processors/precommit-processor.js.map +0 -1
  62. package/distribution/processors/prevote-processor.d.ts +0 -12
  63. package/distribution/processors/prevote-processor.d.ts.map +0 -1
  64. package/distribution/processors/prevote-processor.js +0 -77
  65. package/distribution/processors/prevote-processor.js.map +0 -1
@@ -7,104 +7,120 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
11
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
12
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
13
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
14
- };
15
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
16
- if (kind === "m") throw new TypeError("Private method is not writable");
17
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
18
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
19
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
20
- };
21
- var _Consensus_instances, _Consensus_height, _Consensus_round, _Consensus_step, _Consensus_lockedValue, _Consensus_validValue, _Consensus_didMajorityPrevote, _Consensus_didMajorityPrecommit, _Consensus_isDisposed, _Consensus_pendingJobs, _Consensus_proposalPromise, _Consensus_roundStartTime, _Consensus_handlerLock, _Consensus_isInvalidRoundState, _Consensus_makeProposal, _Consensus_bootstrap, _Consensus_processProposal;
10
+ import { Enums, Events, Identifiers, Locale } from "@mainsail/constants";
22
11
  import { inject, injectable } from "@mainsail/container";
23
- import { Contracts, Events, Identifiers } from "@mainsail/contracts";
24
- import { Utils } from "@mainsail/kernel";
12
+ import { assert, ensureError, Lock } from "@mainsail/utils";
25
13
  import dayjs from "dayjs";
14
+ const FAILED_PROCESSOR_RESULT = {
15
+ feeUsed: 0n,
16
+ gasUsed: 0,
17
+ receipts: new Map(),
18
+ success: false,
19
+ };
26
20
  let Consensus = class Consensus {
27
- constructor() {
28
- _Consensus_instances.add(this);
29
- _Consensus_height.set(this, 1);
30
- _Consensus_round.set(this, 0);
31
- _Consensus_step.set(this, Contracts.Consensus.Step.Propose);
32
- _Consensus_lockedValue.set(this, void 0);
33
- _Consensus_validValue.set(this, void 0);
34
- _Consensus_didMajorityPrevote.set(this, false);
35
- _Consensus_didMajorityPrecommit.set(this, false);
36
- _Consensus_isDisposed.set(this, false);
37
- _Consensus_pendingJobs.set(this, new Set());
38
- _Consensus_proposalPromise.set(this, void 0);
39
- _Consensus_roundStartTime.set(this, 0);
40
- // Handler lock is different than commit lock. It is used to prevent parallel processing and it is similar to queue.
41
- _Consensus_handlerLock.set(this, new Utils.Lock());
42
- }
43
- getHeight() {
44
- return __classPrivateFieldGet(this, _Consensus_height, "f");
21
+ app;
22
+ bootstrapper;
23
+ configuration;
24
+ processor;
25
+ stateStore;
26
+ proposalProcessor;
27
+ messageProcessor;
28
+ scheduler;
29
+ validatorsRepository;
30
+ roundStateRepository;
31
+ commitLock;
32
+ validatorSet;
33
+ blockForger;
34
+ eventDispatcher;
35
+ logger;
36
+ statisticService;
37
+ #blockNumber = 1;
38
+ #round = 0;
39
+ #step = Enums.Consensus.Step.Propose;
40
+ #lockedValue;
41
+ #validValue;
42
+ #didMajorityPrevote = false;
43
+ #didMajorityPrecommit = false;
44
+ #didMajorityPrecommitAndProposalIsMissing = false;
45
+ #isDisposed = false;
46
+ #pendingJobs = new Set();
47
+ #proposedBlock;
48
+ #proposalPromise;
49
+ #roundStartTime = 0;
50
+ // Handler lock is different than commit lock. It is used to prevent parallel processing and it is similar to queue.
51
+ #handlerLock = new Lock();
52
+ getBlockNumber() {
53
+ return this.#blockNumber;
45
54
  }
46
55
  getRound() {
47
- return __classPrivateFieldGet(this, _Consensus_round, "f");
56
+ return this.#round;
48
57
  }
49
58
  // TODO: Only for tests
50
59
  setRound(round) {
51
- __classPrivateFieldSet(this, _Consensus_round, round, "f");
60
+ this.#round = round;
52
61
  }
53
62
  getStep() {
54
- return __classPrivateFieldGet(this, _Consensus_step, "f");
63
+ return this.#step;
55
64
  }
56
65
  // TODO: Only for tests
57
66
  setStep(step) {
58
- __classPrivateFieldSet(this, _Consensus_step, step, "f");
67
+ this.#step = step;
59
68
  }
60
69
  getLockedRound() {
61
- return __classPrivateFieldGet(this, _Consensus_lockedValue, "f") ? __classPrivateFieldGet(this, _Consensus_lockedValue, "f").round : undefined;
70
+ return this.#lockedValue ? this.#lockedValue.round : undefined;
62
71
  }
63
72
  getValidRound() {
64
- return __classPrivateFieldGet(this, _Consensus_validValue, "f") ? __classPrivateFieldGet(this, _Consensus_validValue, "f").round : undefined;
73
+ return this.#validValue ? this.#validValue.round : undefined;
65
74
  }
66
- // TODO: Only for tests
75
+ // Only for tests
67
76
  setValidRound(round) {
68
- __classPrivateFieldSet(this, _Consensus_validValue, round, "f");
77
+ this.#validValue = round;
69
78
  }
70
- // TODO: Only for tests
71
- setProposal(proposalPromise) {
72
- __classPrivateFieldSet(this, _Consensus_proposalPromise, proposalPromise, "f");
79
+ // Only for tests
80
+ setProposal(proposalPromise, block) {
81
+ this.#proposalPromise = proposalPromise;
82
+ this.#proposedBlock = block;
73
83
  }
74
84
  getState() {
75
85
  return {
76
- height: __classPrivateFieldGet(this, _Consensus_height, "f"),
86
+ blockNumber: this.#blockNumber,
77
87
  lockedRound: this.getLockedRound(),
78
- round: __classPrivateFieldGet(this, _Consensus_round, "f"),
79
- step: __classPrivateFieldGet(this, _Consensus_step, "f"),
88
+ round: this.#round,
89
+ step: this.#step,
80
90
  validRound: this.getValidRound(),
81
91
  };
82
92
  }
83
93
  async run() {
84
- await __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_bootstrap).call(this);
85
- await this.startRound(__classPrivateFieldGet(this, _Consensus_round, "f"));
86
- await this.handle(this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f")));
87
- // Rerun previous rounds, in case proposal & +2/3 precommits were received
88
- for (let index = 0; index < __classPrivateFieldGet(this, _Consensus_round, "f"); index++) {
89
- await this.handle(this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), index));
94
+ try {
95
+ await this.#bootstrap();
96
+ await this.startRound(this.#round);
97
+ await this.handle(this.roundStateRepository.getRoundState(this.#blockNumber, this.#round));
98
+ // Rerun previous rounds, in case proposal & +2/3 precommits were received
99
+ for (let index = 0; index < this.#round; index++) {
100
+ await this.handle(this.roundStateRepository.getRoundState(this.#blockNumber, index));
101
+ }
102
+ }
103
+ catch (rawError) {
104
+ const error = ensureError(rawError);
105
+ await this.app.terminate("Consensus bootstrap error", error);
90
106
  }
91
107
  }
92
108
  async dispose() {
93
109
  this.scheduler.clear();
94
- __classPrivateFieldSet(this, _Consensus_isDisposed, true, "f");
95
- await __classPrivateFieldGet(this, _Consensus_handlerLock, "f").runExclusive(async () => { });
110
+ this.#isDisposed = true;
111
+ await this.#handlerLock.runExclusive(async () => { });
96
112
  }
97
113
  async handle(roundState) {
98
- if (__classPrivateFieldGet(this, _Consensus_pendingJobs, "f").has(roundState)) {
114
+ if (this.#pendingJobs.has(roundState)) {
99
115
  return;
100
116
  }
101
- __classPrivateFieldGet(this, _Consensus_pendingJobs, "f").add(roundState);
102
- await __classPrivateFieldGet(this, _Consensus_handlerLock, "f").runExclusive(async () => {
103
- __classPrivateFieldGet(this, _Consensus_pendingJobs, "f").delete(roundState);
104
- if (__classPrivateFieldGet(this, _Consensus_isDisposed, "f")) {
117
+ this.#pendingJobs.add(roundState);
118
+ await this.#handlerLock.runExclusive(async () => {
119
+ this.#pendingJobs.delete(roundState);
120
+ if (this.#isDisposed) {
105
121
  return;
106
122
  }
107
- await __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_processProposal).call(this, roundState);
123
+ await this.#processProposal(roundState);
108
124
  await this.onProposal(roundState);
109
125
  await this.onProposalLocked(roundState);
110
126
  if (roundState.hasMajorityPrevotes()) {
@@ -128,69 +144,72 @@ let Consensus = class Consensus {
128
144
  });
129
145
  }
130
146
  async handleCommitState(commitState) {
131
- await __classPrivateFieldGet(this, _Consensus_handlerLock, "f").runExclusive(async () => {
132
- if (__classPrivateFieldGet(this, _Consensus_isDisposed, "f")) {
147
+ await this.#handlerLock.runExclusive(async () => {
148
+ if (this.#isDisposed) {
133
149
  return;
134
150
  }
135
- await this.onMajorityPrecommit(commitState);
151
+ await this.#processBlock(commitState);
152
+ await this.onMajorityPrecommit(commitState, false);
136
153
  });
137
154
  }
138
155
  async startRound(round) {
139
- __classPrivateFieldSet(this, _Consensus_round, round, "f");
140
- __classPrivateFieldSet(this, _Consensus_step, Contracts.Consensus.Step.Propose, "f");
141
- __classPrivateFieldSet(this, _Consensus_didMajorityPrevote, false, "f");
142
- __classPrivateFieldSet(this, _Consensus_didMajorityPrecommit, false, "f");
143
- __classPrivateFieldSet(this, _Consensus_roundStartTime, dayjs().valueOf(), "f");
156
+ this.#round = round;
157
+ this.#step = Enums.Consensus.Step.Propose;
158
+ this.#didMajorityPrevote = false;
159
+ this.#didMajorityPrecommit = false;
160
+ this.#roundStartTime = dayjs().valueOf();
144
161
  this.scheduler.clear();
145
- if (__classPrivateFieldGet(this, _Consensus_isDisposed, "f")) {
162
+ this.statisticService.newRound(this.#blockNumber, round);
163
+ if (this.#isDisposed) {
146
164
  return;
147
165
  }
148
- const roundState = this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"));
149
- this.logger.info(`>> Starting new round: ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} with proposer: ${roundState.proposer.address}`);
166
+ const roundState = this.roundStateRepository.getRoundState(this.#blockNumber, this.#round);
167
+ this.logger.info(`>> Starting new round: ${this.#getHeightRoundString()} with proposer: ${roundState.proposer.address}`, "consensus");
150
168
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.RoundStarted, this.getState());
151
- this.scheduler.scheduleTimeoutBlockPrepare(this.scheduler.getNextBlockTimestamp(__classPrivateFieldGet(this, _Consensus_roundStartTime, "f")));
169
+ this.scheduler.scheduleTimeoutBlockPrepare(this.scheduler.getNextBlockTimestamp(this.#roundStartTime));
152
170
  // TODO: Skip on sync
153
171
  await this.propose(roundState);
154
172
  }
155
173
  async onTimeoutStartRound() {
156
- this.scheduler.scheduleTimeoutPropose(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"));
157
- if (__classPrivateFieldGet(this, _Consensus_proposalPromise, "f")) {
158
- const proposal = await __classPrivateFieldGet(this, _Consensus_proposalPromise, "f");
159
- __classPrivateFieldSet(this, _Consensus_proposalPromise, undefined, "f");
174
+ this.scheduler.scheduleTimeoutPropose(this.#blockNumber, this.#round);
175
+ if (this.#proposalPromise) {
176
+ const proposal = await this.#proposalPromise;
177
+ assert.defined(this.#proposedBlock);
178
+ this.logger.info(`Proposing block ${this.#getBlockString(this.#proposedBlock)}`, "consensus");
179
+ this.#proposalPromise = undefined;
180
+ this.#proposedBlock = undefined;
160
181
  await this.proposalProcessor.process(proposal);
161
182
  }
162
183
  }
163
184
  async onProposal(roundState) {
164
185
  const proposal = roundState.getProposal();
165
- if (__classPrivateFieldGet(this, _Consensus_step, "f") !== Contracts.Consensus.Step.Propose ||
166
- __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_isInvalidRoundState).call(this, roundState) ||
186
+ if (this.#step !== Enums.Consensus.Step.Propose ||
187
+ this.#isInvalidRoundState(roundState) ||
167
188
  !proposal ||
168
189
  proposal.validRound !== undefined) {
169
190
  return;
170
191
  }
171
- __classPrivateFieldSet(this, _Consensus_step, Contracts.Consensus.Step.Prevote, "f");
172
- const { block } = proposal.getData();
173
- this.logger.info(`Received proposal ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} blockId: ${block.data.id}`);
192
+ this.#step = Enums.Consensus.Step.Prevote;
193
+ this.logger.info(`Received proposal ${this.#getBlockString(proposal.blockHeader)}`, "consensus");
174
194
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.ProposalAccepted, this.getState());
175
- await this.prevote(roundState.getProcessorResult() ? block.data.id : undefined);
195
+ await this.prevote(roundState.getProcessorResult().success ? proposal.blockHeader.hash : undefined);
176
196
  }
177
197
  async onProposalLocked(roundState) {
178
198
  const proposal = roundState.getProposal();
179
- if (__classPrivateFieldGet(this, _Consensus_step, "f") !== Contracts.Consensus.Step.Propose ||
180
- __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_isInvalidRoundState).call(this, roundState) ||
199
+ if (this.#step !== Enums.Consensus.Step.Propose ||
200
+ this.#isInvalidRoundState(roundState) ||
181
201
  !proposal ||
182
- !proposal.getData().lockProof ||
202
+ !proposal.lockProof ||
183
203
  proposal.validRound === undefined ||
184
- proposal.validRound >= __classPrivateFieldGet(this, _Consensus_round, "f")) {
204
+ proposal.validRound >= this.#round) {
185
205
  return;
186
206
  }
187
- const { block } = proposal.getData();
188
- __classPrivateFieldSet(this, _Consensus_step, Contracts.Consensus.Step.Prevote, "f");
189
- this.logger.info(`Received proposal ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} with locked blockId: ${block.data.id}`);
207
+ this.#step = Enums.Consensus.Step.Prevote;
208
+ this.logger.info(`Received locked proposal ${this.#getBlockString(proposal.blockHeader)}`, "consensus");
190
209
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.ProposalAccepted, this.getState());
191
210
  const lockedRound = this.getLockedRound();
192
- if ((!lockedRound || lockedRound <= proposal.validRound) && roundState.getProcessorResult()) {
193
- await this.prevote(block.data.id);
211
+ if ((!lockedRound || lockedRound <= proposal.validRound) && roundState.getProcessorResult().success) {
212
+ await this.prevote(proposal.blockHeader.hash);
194
213
  }
195
214
  else {
196
215
  await this.prevote();
@@ -198,119 +217,142 @@ let Consensus = class Consensus {
198
217
  }
199
218
  async onMajorityPrevote(roundState) {
200
219
  const proposal = roundState.getProposal();
201
- if (__classPrivateFieldGet(this, _Consensus_didMajorityPrevote, "f") ||
202
- __classPrivateFieldGet(this, _Consensus_step, "f") === Contracts.Consensus.Step.Propose ||
203
- __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_isInvalidRoundState).call(this, roundState) ||
220
+ if (this.#didMajorityPrevote ||
221
+ this.#step === Enums.Consensus.Step.Propose ||
222
+ this.#isInvalidRoundState(roundState) ||
204
223
  !proposal ||
205
224
  !roundState.getProcessorResult().success) {
206
225
  return;
207
226
  }
208
- const { block } = proposal.getData();
209
- this.logger.info(`Received +2/3 prevotes for ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} blockId: ${block.data.id}`);
210
- __classPrivateFieldSet(this, _Consensus_didMajorityPrevote, true, "f");
211
- if (__classPrivateFieldGet(this, _Consensus_step, "f") === Contracts.Consensus.Step.Prevote) {
212
- __classPrivateFieldSet(this, _Consensus_lockedValue, roundState, "f");
213
- __classPrivateFieldSet(this, _Consensus_validValue, roundState, "f");
214
- __classPrivateFieldSet(this, _Consensus_step, Contracts.Consensus.Step.Precommit, "f");
227
+ this.logger.info(`Received +2/3 prevotes for ${this.#getBlockString(proposal.blockHeader)}`, "consensus");
228
+ this.#didMajorityPrevote = true;
229
+ if (this.#step === Enums.Consensus.Step.Prevote) {
230
+ this.#lockedValue = roundState;
231
+ this.#validValue = roundState;
232
+ this.#step = Enums.Consensus.Step.Precommit;
215
233
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.PrevotedProposal, this.getState());
216
- await this.precommit(block.data.id);
234
+ await this.precommit(proposal.blockHeader.hash);
217
235
  }
218
236
  else {
219
- __classPrivateFieldSet(this, _Consensus_validValue, roundState, "f");
237
+ this.#validValue = roundState;
220
238
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.PrevotedProposal, this.getState());
221
239
  }
222
240
  }
223
241
  async onMajorityPrevoteAny(roundState) {
224
- if (__classPrivateFieldGet(this, _Consensus_step, "f") !== Contracts.Consensus.Step.Prevote || __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_isInvalidRoundState).call(this, roundState)) {
242
+ if (this.#step !== Enums.Consensus.Step.Prevote || this.#isInvalidRoundState(roundState)) {
225
243
  return;
226
244
  }
227
- if (this.scheduler.scheduleTimeoutPrevote(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"))) {
245
+ if (this.scheduler.scheduleTimeoutPrevote(this.#blockNumber, this.#round)) {
228
246
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.PrevotedAny, this.getState());
229
247
  }
230
248
  }
231
249
  async onMajorityPrevoteNull(roundState) {
232
- if (__classPrivateFieldGet(this, _Consensus_step, "f") !== Contracts.Consensus.Step.Prevote || __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_isInvalidRoundState).call(this, roundState)) {
250
+ if (this.#step !== Enums.Consensus.Step.Prevote || this.#isInvalidRoundState(roundState)) {
233
251
  return;
234
252
  }
235
- this.logger.info(`Received +2/3 prevotes for ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} blockId: null`);
236
- __classPrivateFieldSet(this, _Consensus_step, Contracts.Consensus.Step.Precommit, "f");
253
+ this.logger.info(`Received +2/3 prevotes for ${this.#getHeightRoundString()}/null`, "consensus");
254
+ this.#step = Enums.Consensus.Step.Precommit;
237
255
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.PrevotedNull, this.getState());
238
256
  await this.precommit();
239
257
  }
240
258
  async onMajorityPrecommitAny(roundState) {
241
- if (__classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_isInvalidRoundState).call(this, roundState)) {
259
+ if (this.#isInvalidRoundState(roundState)) {
242
260
  return;
243
261
  }
244
- if (this.scheduler.scheduleTimeoutPrecommit(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"))) {
262
+ if (this.scheduler.scheduleTimeoutPrecommit(this.#blockNumber, this.#round)) {
245
263
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.PrecommitedAny, this.getState());
246
264
  }
247
265
  }
248
- async onMajorityPrecommit(roundState) {
249
- // TODO: Only height must match. Round can be any. Add tests
250
- if (__classPrivateFieldGet(this, _Consensus_didMajorityPrecommit, "f") || roundState.height !== __classPrivateFieldGet(this, _Consensus_height, "f")) {
266
+ async onMajorityPrecommit(processState, isRoundState = true) {
267
+ // TODO: Only block number must match. Round can be any. Add tests
268
+ if ((isRoundState && this.#didMajorityPrecommit) || processState.blockNumber !== this.#blockNumber) {
251
269
  return;
252
270
  }
253
- __classPrivateFieldSet(this, _Consensus_didMajorityPrecommit, true, "f");
254
- const block = roundState.getBlock();
255
- if (!roundState.getProcessorResult().success) {
256
- this.logger.info(`Block ${block.data.id} on height ${__classPrivateFieldGet(this, _Consensus_height, "f")} received +2/3 precommits but is invalid`);
271
+ if (processState.hasProcessorResult() === false) {
272
+ if (this.#didMajorityPrecommitAndProposalIsMissing) {
273
+ return;
274
+ }
275
+ this.logger.info(`Received +2/3 precommits for ${this.#getHeightRoundString()}, but proposal is missing`, "consensus");
276
+ this.#didMajorityPrecommitAndProposalIsMissing = true;
277
+ return;
278
+ }
279
+ if (isRoundState) {
280
+ // Sets it only once for round state
281
+ this.#didMajorityPrecommit = true;
282
+ }
283
+ const block = processState.getBlock();
284
+ this.logger.info(`Received +2/3 precommits for ${this.#getBlockString(block)}`, "consensus");
285
+ if (!processState.getProcessorResult().success) {
286
+ this.logger.info(`Block ${this.#getBlockString(block)} is invalid`, "consensus");
257
287
  return;
258
288
  }
259
- this.logger.info(`Received +2/3 precommits for ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${roundState.round} blockId: ${block.data.id}`);
260
289
  await this.eventDispatcher.dispatch(Events.ConsensusEvent.PrecommitedProposal, this.getState());
261
290
  await this.commitLock.runExclusive(async () => {
262
- var _a;
263
291
  try {
264
- await this.processor.commit(roundState);
292
+ await this.processor.commit(processState);
265
293
  }
266
- catch (error) {
294
+ catch (rawError) {
295
+ const error = ensureError(rawError);
267
296
  await this.app.terminate("Failed to commit block", error);
268
297
  }
269
298
  this.roundStateRepository.clear();
270
- __classPrivateFieldSet(this, _Consensus_height, (_a = __classPrivateFieldGet(this, _Consensus_height, "f"), _a++, _a), "f");
271
- __classPrivateFieldSet(this, _Consensus_lockedValue, undefined, "f");
272
- __classPrivateFieldSet(this, _Consensus_validValue, undefined, "f");
299
+ this.#blockNumber++;
300
+ this.#lockedValue = undefined;
301
+ this.#validValue = undefined;
273
302
  await this.startRound(0);
274
303
  });
275
304
  }
276
305
  async onMinorityWithHigherRound(roundState) {
277
- if (roundState.height !== __classPrivateFieldGet(this, _Consensus_height, "f") || roundState.round <= __classPrivateFieldGet(this, _Consensus_round, "f")) {
306
+ if (roundState.blockNumber !== this.#blockNumber || roundState.round <= this.#round) {
278
307
  return;
279
308
  }
280
309
  await this.startRound(roundState.round);
281
310
  }
282
- async onTimeoutPropose(height, round) {
283
- await __classPrivateFieldGet(this, _Consensus_handlerLock, "f").runExclusive(async () => {
284
- if (__classPrivateFieldGet(this, _Consensus_step, "f") !== Contracts.Consensus.Step.Propose || __classPrivateFieldGet(this, _Consensus_height, "f") !== height || __classPrivateFieldGet(this, _Consensus_round, "f") !== round) {
311
+ async onTimeoutPropose(blockNumber, round) {
312
+ await this.#handlerLock.runExclusive(async () => {
313
+ if (this.#step !== Enums.Consensus.Step.Propose ||
314
+ this.#blockNumber !== blockNumber ||
315
+ this.#round !== round) {
285
316
  return;
286
317
  }
287
- this.logger.info(`Timeout to propose ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} expired`);
288
- __classPrivateFieldSet(this, _Consensus_step, Contracts.Consensus.Step.Prevote, "f");
318
+ this.logger.info(`Timeout to propose ${this.#getHeightRoundString()} expired`, "consensus");
319
+ this.#step = Enums.Consensus.Step.Prevote;
289
320
  await this.prevote();
290
321
  });
291
322
  }
292
- async onTimeoutPrevote(height, round) {
293
- await __classPrivateFieldGet(this, _Consensus_handlerLock, "f").runExclusive(async () => {
294
- if (__classPrivateFieldGet(this, _Consensus_step, "f") !== Contracts.Consensus.Step.Prevote || __classPrivateFieldGet(this, _Consensus_height, "f") !== height || __classPrivateFieldGet(this, _Consensus_round, "f") !== round) {
323
+ async onTimeoutPrevote(blockNumber, round) {
324
+ await this.#handlerLock.runExclusive(async () => {
325
+ if (this.#step !== Enums.Consensus.Step.Prevote ||
326
+ this.#blockNumber !== blockNumber ||
327
+ this.#round !== round) {
295
328
  return;
296
329
  }
297
- this.logger.info(`Timeout to prevote ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} expired`);
298
- this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f")).logPrevotes();
299
- __classPrivateFieldSet(this, _Consensus_step, Contracts.Consensus.Step.Precommit, "f");
330
+ this.logger.info(`Timeout to prevote ${this.#getHeightRoundString()} expired`, "consensus");
331
+ this.roundStateRepository.getRoundState(this.#blockNumber, this.#round).logPrevotes();
332
+ this.#step = Enums.Consensus.Step.Precommit;
300
333
  await this.precommit();
301
334
  });
302
335
  }
303
- async onTimeoutPrecommit(height, round) {
304
- await __classPrivateFieldGet(this, _Consensus_handlerLock, "f").runExclusive(async () => {
305
- if (__classPrivateFieldGet(this, _Consensus_height, "f") !== height || __classPrivateFieldGet(this, _Consensus_round, "f") !== round) {
336
+ async onTimeoutPrecommit(blockNumber, round) {
337
+ await this.#handlerLock.runExclusive(async () => {
338
+ if (this.#blockNumber !== blockNumber || this.#round !== round) {
306
339
  return;
307
340
  }
308
- this.logger.info(`Timeout to precommit ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} expired`);
309
- this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f")).logPrevotes();
310
- this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f")).logPrecommits();
311
- await this.startRound(__classPrivateFieldGet(this, _Consensus_round, "f") + 1);
341
+ this.logger.info(`Timeout to precommit ${this.#getHeightRoundString()} expired`, "consensus");
342
+ this.roundStateRepository.getRoundState(this.#blockNumber, this.#round).logPrevotes();
343
+ this.roundStateRepository.getRoundState(this.#blockNumber, this.#round).logPrecommits();
344
+ await this.startRound(this.#round + 1);
312
345
  });
313
346
  }
347
+ #isInvalidRoundState(roundState) {
348
+ if (roundState.blockNumber !== this.#blockNumber) {
349
+ return true;
350
+ }
351
+ if (roundState.round !== this.#round) {
352
+ return true;
353
+ }
354
+ return false;
355
+ }
314
356
  async propose(roundState) {
315
357
  if (roundState.hasProposal()) {
316
358
  return;
@@ -319,12 +361,24 @@ let Consensus = class Consensus {
319
361
  if (registeredProposer === undefined) {
320
362
  return;
321
363
  }
322
- this.logger.info(`Found registered proposer: ${roundState.proposer.address}`);
323
- __classPrivateFieldSet(this, _Consensus_proposalPromise, __classPrivateFieldGet(this, _Consensus_instances, "m", _Consensus_makeProposal).call(this, roundState, registeredProposer), "f");
364
+ this.logger.info(`Found registered proposer: ${roundState.proposer.address}`, "consensus");
365
+ this.#proposalPromise = this.#makeProposal(roundState, registeredProposer);
366
+ }
367
+ async #makeProposal(roundState, registeredProposer) {
368
+ if (this.#validValue) {
369
+ this.#proposedBlock = this.#validValue.getBlock();
370
+ const lockProof = await this.#validValue.aggregatePrevotes();
371
+ this.logger.info(`Created proposal with existing block ${this.#getBlockString(this.#proposedBlock)}`, "consensus");
372
+ return await registeredProposer.propose(this.validatorSet.getValidatorIndexByWalletAddress(roundState.proposer.address), this.#round, this.#validValue.round, this.#proposedBlock, lockProof);
373
+ }
374
+ this.#proposedBlock = this.#proposedBlock = await this.blockForger.forgeBlock(roundState.proposer.address, this.#round, this.scheduler.getNextBlockTimestamp(this.#roundStartTime));
375
+ this.logger.info(`Created proposal with new block ${this.#getBlockString(this.#proposedBlock)}`, "consensus");
376
+ void this.eventDispatcher.dispatch(Events.BlockEvent.Forged, this.#proposedBlock);
377
+ return registeredProposer.propose(this.validatorSet.getValidatorIndexByWalletAddress(roundState.proposer.address), this.#round, undefined, this.#proposedBlock);
324
378
  }
325
379
  async prevote(value) {
326
- const roundState = this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"));
327
- for (const validator of this.validatorSet.getActiveValidators()) {
380
+ const roundState = this.roundStateRepository.getRoundState(this.#blockNumber, this.#round);
381
+ for (const validator of this.validatorSet.getRoundValidators()) {
328
382
  const localValidator = this.validatorsRepository.getValidator(validator.blsPublicKey);
329
383
  if (localValidator === undefined) {
330
384
  continue;
@@ -333,13 +387,13 @@ let Consensus = class Consensus {
333
387
  if (roundState.hasPrevote(validatorIndex)) {
334
388
  continue;
335
389
  }
336
- const prevote = await localValidator.prevote(validatorIndex, __classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"), value);
337
- void this.prevoteProcessor.process(prevote);
390
+ const prevote = await localValidator.prevote(validatorIndex, this.#blockNumber, this.#round, value);
391
+ void this.messageProcessor.process(prevote);
338
392
  }
339
393
  }
340
394
  async precommit(value) {
341
- const roundState = this.roundStateRepository.getRoundState(__classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"));
342
- for (const validator of this.validatorSet.getActiveValidators()) {
395
+ const roundState = this.roundStateRepository.getRoundState(this.#blockNumber, this.#round);
396
+ for (const validator of this.validatorSet.getRoundValidators()) {
343
397
  const localValidator = this.validatorsRepository.getValidator(validator.blsPublicKey);
344
398
  if (localValidator === undefined) {
345
399
  continue;
@@ -348,82 +402,74 @@ let Consensus = class Consensus {
348
402
  if (roundState.hasPrecommit(validatorIndex)) {
349
403
  continue;
350
404
  }
351
- const precommit = await localValidator.precommit(validatorIndex, __classPrivateFieldGet(this, _Consensus_height, "f"), __classPrivateFieldGet(this, _Consensus_round, "f"), value);
352
- void this.precommitProcessor.process(precommit);
405
+ const precommit = await localValidator.precommit(validatorIndex, this.#blockNumber, this.#round, value);
406
+ void this.messageProcessor.process(precommit);
353
407
  }
354
408
  }
355
- };
356
- _Consensus_height = new WeakMap();
357
- _Consensus_round = new WeakMap();
358
- _Consensus_step = new WeakMap();
359
- _Consensus_lockedValue = new WeakMap();
360
- _Consensus_validValue = new WeakMap();
361
- _Consensus_didMajorityPrevote = new WeakMap();
362
- _Consensus_didMajorityPrecommit = new WeakMap();
363
- _Consensus_isDisposed = new WeakMap();
364
- _Consensus_pendingJobs = new WeakMap();
365
- _Consensus_proposalPromise = new WeakMap();
366
- _Consensus_roundStartTime = new WeakMap();
367
- _Consensus_handlerLock = new WeakMap();
368
- _Consensus_instances = new WeakSet();
369
- _Consensus_isInvalidRoundState = function _Consensus_isInvalidRoundState(roundState) {
370
- if (roundState.height !== __classPrivateFieldGet(this, _Consensus_height, "f")) {
371
- return true;
372
- }
373
- if (roundState.round !== __classPrivateFieldGet(this, _Consensus_round, "f")) {
374
- return true;
375
- }
376
- return false;
377
- };
378
- _Consensus_makeProposal = async function _Consensus_makeProposal(roundState, registeredProposer) {
379
- if (__classPrivateFieldGet(this, _Consensus_validValue, "f")) {
380
- const block = __classPrivateFieldGet(this, _Consensus_validValue, "f").getBlock();
381
- const lockProof = await __classPrivateFieldGet(this, _Consensus_validValue, "f").aggregatePrevotes();
382
- this.logger.info(`Proposing valid block ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} from round ${this.getValidRound()} with blockId: ${block.data.id}`);
383
- return await registeredProposer.propose(this.validatorSet.getValidatorIndexByWalletAddress(roundState.proposer.address), __classPrivateFieldGet(this, _Consensus_round, "f"), __classPrivateFieldGet(this, _Consensus_validValue, "f").round, block, lockProof);
384
- }
385
- const block = await registeredProposer.prepareBlock(roundState.proposer.address, __classPrivateFieldGet(this, _Consensus_round, "f"), this.scheduler.getNextBlockTimestamp(__classPrivateFieldGet(this, _Consensus_roundStartTime, "f")));
386
- this.logger.info(`Proposing new block ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")} with blockId: ${block.data.id}`);
387
- void this.eventDispatcher.dispatch(Events.BlockEvent.Forged, block.data);
388
- return registeredProposer.propose(this.validatorSet.getValidatorIndexByWalletAddress(roundState.proposer.address), __classPrivateFieldGet(this, _Consensus_round, "f"), undefined, block);
389
- };
390
- _Consensus_bootstrap = async function _Consensus_bootstrap() {
391
- const state = await this.bootstrapper.run();
392
- if (state && state.height === this.stateStore.getLastBlock().data.height + 1) {
393
- __classPrivateFieldSet(this, _Consensus_step, state.step, "f");
394
- __classPrivateFieldSet(this, _Consensus_height, state.height, "f");
395
- __classPrivateFieldSet(this, _Consensus_round, state.round, "f");
396
- __classPrivateFieldSet(this, _Consensus_lockedValue, state.lockedValue, "f");
397
- __classPrivateFieldSet(this, _Consensus_validValue, state.validValue, "f");
398
- }
399
- else {
409
+ async #bootstrap() {
410
+ this.#blockNumber = this.stateStore.getLastBlock().number + 1;
411
+ const state = await this.bootstrapper.run();
400
412
  if (state) {
401
- this.logger.warning(`Skipping state restore, because stored height is ${state.height}, but should be ${this.stateStore.getLastBlock().data.height + 1}`);
402
- this.roundStateRepository.clear();
413
+ if (state.blockNumber === this.#blockNumber) {
414
+ this.#step = state.step;
415
+ this.#round = state.round;
416
+ this.#lockedValue = state.lockedValue;
417
+ this.#validValue = state.validValue;
418
+ }
419
+ else {
420
+ const storedBlockNumber = state.blockNumber.toLocaleString(Locale);
421
+ const currentBlockNumber = this.#blockNumber.toLocaleString(Locale);
422
+ this.logger.warn(`Skipping state restore, because stored block number is ${storedBlockNumber}, but should be ${currentBlockNumber}`, "consensus");
423
+ this.roundStateRepository.clear();
424
+ }
425
+ }
426
+ if (this.#blockNumber !== this.configuration.getHeight()) {
427
+ throw new Error(`bootstrapped block number ${this.#blockNumber} does not match configuration block number ${this.configuration.getHeight()}`);
403
428
  }
404
- const lastBlock = this.stateStore.getLastBlock();
405
- __classPrivateFieldSet(this, _Consensus_height, lastBlock.data.height + 1, "f");
429
+ this.logger.info(`Completed consensus bootstrap for ${this.#getHeightRoundString()} with total round ${this.stateStore.getTotalRound()}`, "consensus");
430
+ await this.eventDispatcher.dispatch(Events.ConsensusEvent.Bootstrapped, this.getState());
406
431
  }
407
- if (__classPrivateFieldGet(this, _Consensus_height, "f") !== this.configuration.getHeight()) {
408
- throw new Error(`bootstrapped height ${__classPrivateFieldGet(this, _Consensus_height, "f")} does not match configuration height ${this.configuration.getHeight()}`);
432
+ async #processProposal(roundState) {
433
+ const proposal = roundState.getProposal();
434
+ if (!roundState.hasProcessorResult() && proposal) {
435
+ try {
436
+ await proposal.deserializePayload();
437
+ if (!(await this.proposalProcessor.hasValidLockProof(proposal))) {
438
+ roundState.setProcessorResult(FAILED_PROCESSOR_RESULT);
439
+ return;
440
+ }
441
+ roundState.setProcessorResult(await this.processor.process(roundState));
442
+ }
443
+ catch (rawError) {
444
+ const error = ensureError(rawError);
445
+ this.logger.error(`Failed to process proposal ${this.#getHeightRoundString()}: ${error.message}`, "consensus");
446
+ roundState.setProcessorResult(FAILED_PROCESSOR_RESULT);
447
+ }
448
+ }
409
449
  }
410
- this.logger.info(`Completed consensus bootstrap for ${__classPrivateFieldGet(this, _Consensus_height, "f")}/${__classPrivateFieldGet(this, _Consensus_round, "f")}/${this.stateStore.getTotalRound()}`);
411
- await this.eventDispatcher.dispatch(Events.ConsensusEvent.Bootstrapped, this.getState());
412
- };
413
- _Consensus_processProposal = async function _Consensus_processProposal(roundState) {
414
- const proposal = roundState.getProposal();
415
- if (!roundState.hasProcessorResult() && proposal) {
416
- try {
417
- await proposal.deserializeData();
418
- if (!(await this.proposalProcessor.hasValidLockProof(proposal))) {
419
- roundState.setProcessorResult({ gasUsed: 0, receipts: new Map(), success: false });
420
- return;
450
+ async #processBlock(commitState) {
451
+ if (!commitState.hasProcessorResult()) {
452
+ try {
453
+ commitState.setProcessorResult(await this.processor.process(commitState));
454
+ }
455
+ catch {
456
+ commitState.setProcessorResult(FAILED_PROCESSOR_RESULT);
421
457
  }
422
- roundState.setProcessorResult(await this.processor.process(roundState));
423
458
  }
424
- catch {
425
- roundState.setProcessorResult({ gasUsed: 0, receipts: new Map(), success: false });
459
+ }
460
+ #getHeightRoundString() {
461
+ const number = this.#blockNumber.toLocaleString(Locale);
462
+ const consensusRound = this.#round.toLocaleString(Locale);
463
+ return `${number}/${consensusRound}`;
464
+ }
465
+ #getBlockString(block) {
466
+ const number = this.#blockNumber.toLocaleString(Locale);
467
+ const consensusRound = this.#round.toLocaleString(Locale);
468
+ const blockRound = block.round.toLocaleString(Locale);
469
+ if (block.round !== this.#round) {
470
+ return `${number}/${consensusRound}(${blockRound})/${block.hash}`;
426
471
  }
472
+ return `${number}/${consensusRound}/${block.hash}`;
427
473
  }
428
474
  };
429
475
  __decorate([
@@ -451,13 +497,9 @@ __decorate([
451
497
  __metadata("design:type", Object)
452
498
  ], Consensus.prototype, "proposalProcessor", void 0);
453
499
  __decorate([
454
- inject(Identifiers.Consensus.Processor.PreVote),
455
- __metadata("design:type", Object)
456
- ], Consensus.prototype, "prevoteProcessor", void 0);
457
- __decorate([
458
- inject(Identifiers.Consensus.Processor.PreCommit),
500
+ inject(Identifiers.Consensus.Processor.Message),
459
501
  __metadata("design:type", Object)
460
- ], Consensus.prototype, "precommitProcessor", void 0);
502
+ ], Consensus.prototype, "messageProcessor", void 0);
461
503
  __decorate([
462
504
  inject(Identifiers.Consensus.Scheduler),
463
505
  __metadata("design:type", Object)
@@ -478,6 +520,10 @@ __decorate([
478
520
  inject(Identifiers.ValidatorSet.Service),
479
521
  __metadata("design:type", Object)
480
522
  ], Consensus.prototype, "validatorSet", void 0);
523
+ __decorate([
524
+ inject(Identifiers.Forger.Block),
525
+ __metadata("design:type", Object)
526
+ ], Consensus.prototype, "blockForger", void 0);
481
527
  __decorate([
482
528
  inject(Identifiers.Services.EventDispatcher.Service),
483
529
  __metadata("design:type", Object)
@@ -486,6 +532,10 @@ __decorate([
486
532
  inject(Identifiers.Services.Log.Service),
487
533
  __metadata("design:type", Object)
488
534
  ], Consensus.prototype, "logger", void 0);
535
+ __decorate([
536
+ inject(Identifiers.P2P.Statistic.Service),
537
+ __metadata("design:type", Object)
538
+ ], Consensus.prototype, "statisticService", void 0);
489
539
  Consensus = __decorate([
490
540
  injectable()
491
541
  ], Consensus);