@mainsail/consensus 0.0.1-evm.5 → 0.0.1-evm.50
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/distribution/aggregator.d.ts +3 -4
- package/distribution/aggregator.d.ts.map +1 -1
- package/distribution/aggregator.js +11 -9
- package/distribution/aggregator.js.map +1 -1
- package/distribution/bootstrapper.d.ts +1 -1
- package/distribution/bootstrapper.d.ts.map +1 -1
- package/distribution/bootstrapper.js +15 -16
- package/distribution/bootstrapper.js.map +1 -1
- package/distribution/commit-state.d.ts +2 -3
- package/distribution/commit-state.d.ts.map +1 -1
- package/distribution/commit-state.js +21 -41
- package/distribution/commit-state.js.map +1 -1
- package/distribution/consensus.d.ts +10 -9
- package/distribution/consensus.d.ts.map +1 -1
- package/distribution/consensus.js +274 -224
- package/distribution/consensus.js.map +1 -1
- package/distribution/index.d.ts +1 -5
- package/distribution/index.d.ts.map +1 -1
- package/distribution/index.js +1 -30
- package/distribution/index.js.map +1 -1
- package/distribution/processors/abstract-processor.d.ts +5 -5
- package/distribution/processors/abstract-processor.d.ts.map +1 -1
- package/distribution/processors/abstract-processor.js +11 -7
- package/distribution/processors/abstract-processor.js.map +1 -1
- package/distribution/processors/commit-processor.d.ts +1 -2
- package/distribution/processors/commit-processor.d.ts.map +1 -1
- package/distribution/processors/commit-processor.js +29 -46
- package/distribution/processors/commit-processor.js.map +1 -1
- package/distribution/processors/index.d.ts +1 -2
- package/distribution/processors/index.d.ts.map +1 -1
- package/distribution/processors/index.js +1 -2
- package/distribution/processors/index.js.map +1 -1
- package/distribution/processors/message-processor.d.ts +13 -0
- package/distribution/processors/message-processor.d.ts.map +1 -0
- package/distribution/processors/message-processor.js +117 -0
- package/distribution/processors/message-processor.js.map +1 -0
- package/distribution/processors/proposal-processor.d.ts +3 -2
- package/distribution/processors/proposal-processor.d.ts.map +1 -1
- package/distribution/processors/proposal-processor.js +46 -41
- package/distribution/processors/proposal-processor.js.map +1 -1
- package/distribution/round-state-repository.d.ts +2 -2
- package/distribution/round-state-repository.d.ts.map +1 -1
- package/distribution/round-state-repository.js +13 -23
- package/distribution/round-state-repository.js.map +1 -1
- package/distribution/round-state.d.ts +14 -10
- package/distribution/round-state.d.ts.map +1 -1
- package/distribution/round-state.js +173 -153
- package/distribution/round-state.js.map +1 -1
- package/distribution/scheduler.d.ts +1 -1
- package/distribution/scheduler.d.ts.map +1 -1
- package/distribution/scheduler.js +49 -65
- package/distribution/scheduler.js.map +1 -1
- package/distribution/service-provider.d.ts +6 -0
- package/distribution/service-provider.d.ts.map +1 -0
- package/distribution/service-provider.js +42 -0
- package/distribution/service-provider.js.map +1 -0
- package/package.json +14 -11
- package/distribution/processors/precommit-processor.d.ts +0 -12
- package/distribution/processors/precommit-processor.d.ts.map +0 -1
- package/distribution/processors/precommit-processor.js +0 -77
- package/distribution/processors/precommit-processor.js.map +0 -1
- package/distribution/processors/prevote-processor.d.ts +0 -12
- package/distribution/processors/prevote-processor.d.ts.map +0 -1
- package/distribution/processors/prevote-processor.js +0 -77
- 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
|
-
|
|
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 {
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
56
|
+
return this.#round;
|
|
48
57
|
}
|
|
49
58
|
// TODO: Only for tests
|
|
50
59
|
setRound(round) {
|
|
51
|
-
|
|
60
|
+
this.#round = round;
|
|
52
61
|
}
|
|
53
62
|
getStep() {
|
|
54
|
-
return
|
|
63
|
+
return this.#step;
|
|
55
64
|
}
|
|
56
65
|
// TODO: Only for tests
|
|
57
66
|
setStep(step) {
|
|
58
|
-
|
|
67
|
+
this.#step = step;
|
|
59
68
|
}
|
|
60
69
|
getLockedRound() {
|
|
61
|
-
return
|
|
70
|
+
return this.#lockedValue ? this.#lockedValue.round : undefined;
|
|
62
71
|
}
|
|
63
72
|
getValidRound() {
|
|
64
|
-
return
|
|
73
|
+
return this.#validValue ? this.#validValue.round : undefined;
|
|
65
74
|
}
|
|
66
|
-
//
|
|
75
|
+
// Only for tests
|
|
67
76
|
setValidRound(round) {
|
|
68
|
-
|
|
77
|
+
this.#validValue = round;
|
|
69
78
|
}
|
|
70
|
-
//
|
|
71
|
-
setProposal(proposalPromise) {
|
|
72
|
-
|
|
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
|
-
|
|
86
|
+
blockNumber: this.#blockNumber,
|
|
77
87
|
lockedRound: this.getLockedRound(),
|
|
78
|
-
round:
|
|
79
|
-
step:
|
|
88
|
+
round: this.#round,
|
|
89
|
+
step: this.#step,
|
|
80
90
|
validRound: this.getValidRound(),
|
|
81
91
|
};
|
|
82
92
|
}
|
|
83
93
|
async run() {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
95
|
-
await
|
|
110
|
+
this.#isDisposed = true;
|
|
111
|
+
await this.#handlerLock.runExclusive(async () => { });
|
|
96
112
|
}
|
|
97
113
|
async handle(roundState) {
|
|
98
|
-
if (
|
|
114
|
+
if (this.#pendingJobs.has(roundState)) {
|
|
99
115
|
return;
|
|
100
116
|
}
|
|
101
|
-
|
|
102
|
-
await
|
|
103
|
-
|
|
104
|
-
if (
|
|
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
|
|
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
|
|
132
|
-
if (
|
|
147
|
+
await this.#handlerLock.runExclusive(async () => {
|
|
148
|
+
if (this.#isDisposed) {
|
|
133
149
|
return;
|
|
134
150
|
}
|
|
135
|
-
await this
|
|
151
|
+
await this.#processBlock(commitState);
|
|
152
|
+
await this.onMajorityPrecommit(commitState, false);
|
|
136
153
|
});
|
|
137
154
|
}
|
|
138
155
|
async startRound(round) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
162
|
+
this.statisticService.newRound(this.#blockNumber, round);
|
|
163
|
+
if (this.#isDisposed) {
|
|
146
164
|
return;
|
|
147
165
|
}
|
|
148
|
-
const roundState = this.roundStateRepository.getRoundState(
|
|
149
|
-
this.logger.info(`>> Starting new round: ${
|
|
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(
|
|
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(
|
|
157
|
-
if (
|
|
158
|
-
const proposal = await
|
|
159
|
-
|
|
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 (
|
|
166
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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() ?
|
|
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 (
|
|
180
|
-
|
|
199
|
+
if (this.#step !== Enums.Consensus.Step.Propose ||
|
|
200
|
+
this.#isInvalidRoundState(roundState) ||
|
|
181
201
|
!proposal ||
|
|
182
|
-
!proposal.
|
|
202
|
+
!proposal.lockProof ||
|
|
183
203
|
proposal.validRound === undefined ||
|
|
184
|
-
proposal.validRound >=
|
|
204
|
+
proposal.validRound >= this.#round) {
|
|
185
205
|
return;
|
|
186
206
|
}
|
|
187
|
-
|
|
188
|
-
|
|
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(
|
|
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 (
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
209
|
-
this
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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(
|
|
234
|
+
await this.precommit(proposal.blockHeader.hash);
|
|
217
235
|
}
|
|
218
236
|
else {
|
|
219
|
-
|
|
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 (
|
|
242
|
+
if (this.#step !== Enums.Consensus.Step.Prevote || this.#isInvalidRoundState(roundState)) {
|
|
225
243
|
return;
|
|
226
244
|
}
|
|
227
|
-
if (this.scheduler.scheduleTimeoutPrevote(
|
|
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 (
|
|
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 ${
|
|
236
|
-
|
|
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 (
|
|
259
|
+
if (this.#isInvalidRoundState(roundState)) {
|
|
242
260
|
return;
|
|
243
261
|
}
|
|
244
|
-
if (this.scheduler.scheduleTimeoutPrecommit(
|
|
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(
|
|
249
|
-
// TODO: Only
|
|
250
|
-
if (
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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(
|
|
292
|
+
await this.processor.commit(processState);
|
|
265
293
|
}
|
|
266
|
-
catch (
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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.
|
|
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(
|
|
283
|
-
await
|
|
284
|
-
if (
|
|
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 ${
|
|
288
|
-
|
|
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(
|
|
293
|
-
await
|
|
294
|
-
if (
|
|
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 ${
|
|
298
|
-
this.roundStateRepository.getRoundState(
|
|
299
|
-
|
|
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(
|
|
304
|
-
await
|
|
305
|
-
if (
|
|
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 ${
|
|
309
|
-
this.roundStateRepository.getRoundState(
|
|
310
|
-
this.roundStateRepository.getRoundState(
|
|
311
|
-
await this.startRound(
|
|
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
|
-
|
|
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(
|
|
327
|
-
for (const validator of this.validatorSet.
|
|
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,
|
|
337
|
-
void this.
|
|
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(
|
|
342
|
-
for (const validator of this.validatorSet.
|
|
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,
|
|
352
|
-
void this.
|
|
405
|
+
const precommit = await localValidator.precommit(validatorIndex, this.#blockNumber, this.#round, value);
|
|
406
|
+
void this.messageProcessor.process(precommit);
|
|
353
407
|
}
|
|
354
408
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
402
|
-
|
|
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
|
-
|
|
405
|
-
|
|
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
|
-
|
|
408
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
425
|
-
|
|
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.
|
|
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, "
|
|
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);
|