Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Commit a0917cd

Browse files
mar-v-instyppo
authored andcommitted
Race condition in pico failure
1 parent 3777e39 commit a0917cd

File tree

2 files changed

+24
-24
lines changed

2 files changed

+24
-24
lines changed

src/main/generic/api/Client.js

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class Client {
7171
this._consensusOn(consensus, 'transaction-mined', (tx, block, blockNow) => this._onMinedTransaction(block, tx, blockNow));
7272
this._consensusOn(consensus, 'consensus-failed', () => this._onConsensusFailed());
7373

74-
await this._onConsensusChanged(consensus.established ? Client.ConsensusState.ESTABLISHED : Client.ConsensusState.CONNECTING);
74+
this._onConsensusChanged(consensus.established ? Client.ConsensusState.ESTABLISHED : Client.ConsensusState.CONNECTING);
7575

7676
if (this._config.hasFeature(Client.Feature.PASSIVE)) {
7777
consensus.network.allowInboundConnections = true;
@@ -107,7 +107,7 @@ class Client {
107107
this._consensus = newConsensus;
108108
const consensus = await this._consensus;
109109
oldConsensus.handoverTo(consensus);
110-
await this._setupConsensus();
110+
return this._setupConsensus();
111111
}
112112

113113
/**
@@ -181,7 +181,7 @@ class Client {
181181
* @private
182182
*/
183183
_onBlock(blockHash) {
184-
for (const listener of this._blockListeners.valueIterator()) {
184+
for (const listener of this._blockListeners.values()) {
185185
listener(blockHash);
186186
}
187187
}
@@ -191,9 +191,10 @@ class Client {
191191
* @private
192192
*/
193193
_onConsensusChanged(state) {
194-
if (state === this._consensusState) return;
195194
this._consensusSynchronizer.push(async () => {
196195
const consensus = await this._consensus;
196+
if (state === this._consensusState) return;
197+
197198
if (consensus.established) {
198199
const oldSubscription = consensus.getSubscription();
199200
if (oldSubscription.type === Subscription.Type.ADDRESSES) {
@@ -202,17 +203,17 @@ class Client {
202203
}
203204

204205
this._consensusState = state;
205-
for (const listener of this._consensusChangedListeners.valueIterator()) {
206+
for (const listener of this._consensusChangedListeners.values()) {
206207
try {
207208
await listener(state);
208209
} catch (e) {
209210
Log.e(Client, `Error in listener: ${e}`);
210211
}
211212
}
212213

213-
if (state === Client.ConsensusState.ESTABLISHED) {
214+
if (consensus.established) {
214215
const headHash = await consensus.getHeadHash();
215-
for (const listener of this._headChangedListeners.valueIterator()) {
216+
for (const listener of this._headChangedListeners.values()) {
216217
try {
217218
await listener(headHash, 'established', [], [headHash]);
218219
} catch (e) {
@@ -224,15 +225,15 @@ class Client {
224225
}
225226

226227
_onConsensusFailed() {
227-
return this._consensusSynchronizer.push(async () => {
228+
this._consensusSynchronizer.push(async () => {
228229
const consensus = await this._consensus;
229230
if (consensus instanceof PicoConsensus) {
230231
// Upgrade pico consensus to nano consensus
231232
Log.w(Client, 'Pico consensus failed, automatically upgrading to nano consensus');
232233
const newConsensus = new NanoConsensus(await new NanoChain(consensus.network.time), consensus.mempool, consensus.network);
233234
await this._replaceConsensus(Promise.resolve(newConsensus));
234235
}
235-
});
236+
}).catch(Log.e.tag(Client));
236237
}
237238

238239
/**
@@ -246,7 +247,7 @@ class Client {
246247
this._consensusSynchronizer.push(async () => {
247248
// Process head-changed listeners.
248249
if (this._consensusState === Client.ConsensusState.ESTABLISHED) {
249-
for (const listener of this._headChangedListeners.valueIterator()) {
250+
for (const listener of this._headChangedListeners.values()) {
250251
try {
251252
await listener(blockHash, reason, revertedBlocks.map(b => b.hash()), adoptedBlocks.map(b => b.hash()));
252253
} catch (e) {
@@ -265,7 +266,7 @@ class Client {
265266
if (block.isFull()) {
266267
revertedTxs.addAll(block.transactions);
267268
}
268-
// FIXME
269+
// FIXME
269270
for (const tx of this._transactionConfirmWaiting.get(this._txConfirmsAt(block.height)).valueIterator()) {
270271
revertedTxs.add(tx);
271272
}
@@ -328,7 +329,7 @@ class Client {
328329
_onPendingTransaction(tx, blockNow) {
329330
let details;
330331
let fs = [];
331-
for (const {listener, addresses} of this._transactionListeners.valueIterator()) {
332+
for (const {listener, addresses} of this._transactionListeners.values()) {
332333
if (addresses.contains(tx.sender) || addresses.contains(tx.recipient)) {
333334
if (blockNow && blockNow.height >= this._txExpiresAt(tx)) {
334335
details = details || new Client.TransactionDetails(tx, Client.TransactionState.EXPIRED);
@@ -362,7 +363,7 @@ class Client {
362363
_onMinedTransaction(block, tx, blockNow) {
363364
let details;
364365
let fs = [];
365-
for (const {listener, addresses} of this._transactionListeners.valueIterator()) {
366+
for (const {listener, addresses} of this._transactionListeners.values()) {
366367
if (addresses.contains(tx.sender) || addresses.contains(tx.recipient)) {
367368
let state = Client.TransactionState.MINED, confirmations = 1;
368369
if (blockNow) {
@@ -397,7 +398,7 @@ class Client {
397398
_onConfirmedTransaction(block, tx, blockNow) {
398399
let details;
399400
let fs = [];
400-
for (const {listener, addresses} of this._transactionListeners.valueIterator()) {
401+
for (const {listener, addresses} of this._transactionListeners.values()) {
401402
if (addresses.contains(tx.sender) || addresses.contains(tx.recipient)) {
402403
details = details || new Client.TransactionDetails(tx, Client.TransactionState.CONFIRMED, block.hash(), block.height, (blockNow.height - block.height) + 1, block.timestamp);
403404
fs.push(async () => {
@@ -422,7 +423,7 @@ class Client {
422423
_onExpiredTransaction(block, tx) {
423424
let details;
424425
let fs = [];
425-
for (const {listener, addresses} of this._transactionListeners.valueIterator()) {
426+
for (const {listener, addresses} of this._transactionListeners.values()) {
426427
if (addresses.contains(tx.sender) || addresses.contains(tx.recipient)) {
427428
details = details || new Client.TransactionDetails(tx, Client.TransactionState.EXPIRED);
428429
fs.push(async () => {
@@ -910,8 +911,8 @@ class Client {
910911
* @returns {Promise}
911912
*/
912913
waitForConsensusEstablished() {
913-
return new Promise(async resolve => {
914-
if ((await this._consensus).established) {
914+
return new Promise(resolve => {
915+
if (this._consensusState === Client.ConsensusState.ESTABLISHED) {
915916
resolve();
916917
} else {
917918
let handle;

src/test/specs/generic/api/Client.spec.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,18 +175,17 @@ describe('Client', () => {
175175

176176
it('can replace consensus at runtime (pico failure)', async (done) => {
177177
const client = startClient('pico');
178-
let establishedPico = false, syncingNano = false;
179-
client.addConsensusChangedListener((state) => {
180-
if (!establishedPico && state === Client.ConsensusState.ESTABLISHED) {
181-
establishedPico = true;
182-
client._onConsensusFailed();
183-
} else if (establishedPico && state === Client.ConsensusState.SYNCING) {
178+
let syncingNano = false;
179+
await client.waitForConsensusEstablished();
180+
await client.addConsensusChangedListener(async (state) => {
181+
if (state !== Client.ConsensusState.ESTABLISHED) {
184182
syncingNano = true;
185-
} else if (establishedPico && state === Client.ConsensusState.ESTABLISHED) {
183+
} else if (state === Client.ConsensusState.ESTABLISHED) {
186184
expect(syncingNano).toBeTruthy();
187185
done();
188186
}
189187
});
188+
client._onConsensusFailed();
190189
});
191190

192191
});

0 commit comments

Comments
 (0)