mirror of
https://github.com/pezkuwichain/pezkuwi-telemetry.git
synced 2026-05-29 23:31:12 +00:00
Restructure the js app (#243)
* prettier * linter * add prettier, and format the code * remove common, merge it with frontend * refactor the app * better lint and code fix * travis for the frontend app * travis build script Signed-off-by: Daniel Maricic <daniel@woss.io> * lint and build * update the README.md Signed-off-by: Daniel Maricic <daniel@woss.io> * change the commands to reflect refactor Signed-off-by: Daniel Maricic <daniel@woss.io> * prettier and tslint are friends Signed-off-by: Daniel Maricic <daniel@woss.io> * code that wasn't linted properly before Signed-off-by: Daniel Maricic <daniel@woss.io> * prettier rc got deleted * workgin on making the travis pass Signed-off-by: Daniel Maricic <daniel@woss.io> * travis build please? Signed-off-by: Daniel Maricic <daniel@woss.io> * update readme.md Signed-off-by: Daniel Maricic <daniel@woss.io> * dockerfile deleted from fronted - out of scope Signed-off-by: Daniel Maricic <daniel@woss.io> * remove Signed-off-by: Daniel Maricic <daniel@woss.io> * tsconfig Signed-off-by: Daniel Maricic <daniel@woss.io> * found the reason why EOL wasn't happening Signed-off-by: Daniel Maricic <daniel@woss.io> * type for the event in the ConnectionInput as suggested * strictnullCheck to true * noImplicitAny * noUnusedParams * AfgHandling * update * fix Location.tsx * Few minor fixes * remove connection input and revert to original * esnext fixes the imports for icons and non default `* as ` * update to the tsconfig.test.json don't use commonjs please * fixed wrong comment for TIMEOUT_BASE * return totem.svg and type decraration of maybe Signed-off-by: Daniel Maricic <daniel@woss.io> Co-authored-by: Will <w.kopp@kigroup.de>
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
import { Types } from './common';
|
||||
import { State, UpdateBound } from './state';
|
||||
|
||||
// Number of blocks which are kept in memory
|
||||
const BLOCKS_LIMIT = 50;
|
||||
|
||||
export class AfgHandling {
|
||||
private updateState: UpdateBound;
|
||||
private getState: () => Readonly<State>;
|
||||
|
||||
constructor(updateState: UpdateBound, getState: () => Readonly<State>) {
|
||||
this.updateState = updateState;
|
||||
this.getState = getState;
|
||||
}
|
||||
|
||||
public receivedAuthoritySet(
|
||||
authoritySetId: Types.AuthoritySetId,
|
||||
authorities: Types.Authorities
|
||||
) {
|
||||
if (
|
||||
this.getState().authoritySetId != null &&
|
||||
authoritySetId !== this.getState().authoritySetId
|
||||
) {
|
||||
// the visualization is restarted when we receive a new authority set
|
||||
this.updateState({
|
||||
authoritySetId,
|
||||
authorities,
|
||||
consensusInfo: [],
|
||||
displayConsensusLoadingScreen: false,
|
||||
});
|
||||
} else if (this.getState().authoritySetId == null) {
|
||||
// initial display
|
||||
this.updateState({
|
||||
authoritySetId,
|
||||
authorities,
|
||||
consensusInfo: [],
|
||||
displayConsensusLoadingScreen: true,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public receivedFinalized(
|
||||
addr: Types.Address,
|
||||
finalizedNumber: Types.BlockNumber,
|
||||
finalizedHash: Types.BlockHash
|
||||
) {
|
||||
const state = this.getState();
|
||||
if (finalizedNumber < state.best - BLOCKS_LIMIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
Finalized: true,
|
||||
FinalizedHash: finalizedHash,
|
||||
FinalizedHeight: finalizedNumber,
|
||||
|
||||
// this is extrapolated. if this app was just started up we
|
||||
// might not yet have received prevotes/precommits. but
|
||||
// those are a necessary precondition for finalization, so
|
||||
// we can set them and display them in the ui.
|
||||
Prevote: true,
|
||||
Precommit: true,
|
||||
} as Types.ConsensusDetail;
|
||||
this.initialiseConsensusView(
|
||||
state.consensusInfo,
|
||||
finalizedNumber,
|
||||
addr,
|
||||
addr
|
||||
);
|
||||
|
||||
this.updateConsensusInfo(
|
||||
state.consensusInfo,
|
||||
finalizedNumber,
|
||||
addr,
|
||||
addr,
|
||||
data as Partial<Types.ConsensusDetail>
|
||||
);
|
||||
|
||||
// Finalizing a block implicitly includes finalizing all
|
||||
// preceding blocks. This function marks the preceding
|
||||
// blocks as implicitly finalized on and stores a pointer
|
||||
// to the block which contains the explicit finalization.
|
||||
const op = (i: Types.BlockNumber, index: number): boolean => {
|
||||
const consensusDetail = state.consensusInfo[index][1][addr][addr];
|
||||
if (consensusDetail.Finalized || consensusDetail.ImplicitFinalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state.consensusInfo[index][1][addr][addr] = {
|
||||
Finalized: true,
|
||||
FinalizedHeight: i,
|
||||
ImplicitFinalized: true,
|
||||
ImplicitPointer: finalizedNumber,
|
||||
|
||||
// this is extrapolated. if this app was just started up we
|
||||
// might not yet have received prevotes/precommits. but
|
||||
// those are a necessary precondition for finalization, so
|
||||
// we can set them and display them in the ui.
|
||||
Prevote: true,
|
||||
Precommit: true,
|
||||
ImplicitPrevote: true,
|
||||
ImplicitPrecommit: true,
|
||||
};
|
||||
return true;
|
||||
};
|
||||
this.backfill(state.consensusInfo, finalizedNumber, op, addr, addr);
|
||||
|
||||
this.pruneBlocks(state.consensusInfo);
|
||||
this.updateState({ consensusInfo: state.consensusInfo });
|
||||
}
|
||||
|
||||
public receivedPre(
|
||||
addr: Types.Address,
|
||||
height: Types.BlockNumber,
|
||||
voter: Types.Address,
|
||||
what: string
|
||||
) {
|
||||
const state = this.getState();
|
||||
if (height < state.best - BLOCKS_LIMIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = what === 'prevote' ? { Prevote: true } : { Precommit: true };
|
||||
this.initialiseConsensusView(state.consensusInfo, height, addr, voter);
|
||||
this.updateConsensusInfo(
|
||||
state.consensusInfo,
|
||||
height,
|
||||
addr,
|
||||
voter,
|
||||
data as Partial<Types.ConsensusDetail>
|
||||
);
|
||||
|
||||
// A Prevote or Precommit on a block implicitly includes
|
||||
// a vote on all preceding blocks. This function marks
|
||||
// the preceding blocks as implicitly voted on and stores
|
||||
// a pointer to the block which contains the explicit vote.
|
||||
const op = (index: number): boolean => {
|
||||
const consensusDetail = state.consensusInfo[index][1][addr][voter];
|
||||
if (
|
||||
what === 'prevote' &&
|
||||
(consensusDetail.Prevote || consensusDetail.ImplicitPrevote)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
what === 'precommit' &&
|
||||
(consensusDetail.Precommit || consensusDetail.ImplicitPrecommit) &&
|
||||
// because of extrapolation a prevote needs to be set as well.
|
||||
// if it is not we continue backfilling (and set it during that process).
|
||||
(consensusDetail.Prevote || consensusDetail.ImplicitPrevote)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (what === 'prevote') {
|
||||
consensusInfo[index][1][addr][voter].ImplicitPrevote = true;
|
||||
} else if (what === 'precommit') {
|
||||
consensusInfo[index][1][addr][voter].ImplicitPrecommit = true;
|
||||
|
||||
// Extrapolate. Precommit implies Prevote.
|
||||
consensusInfo[index][1][addr][voter].ImplicitPrevote = true;
|
||||
}
|
||||
consensusInfo[index][1][addr][voter].ImplicitPointer = height;
|
||||
return true;
|
||||
};
|
||||
const consensusInfo = this.getState().consensusInfo;
|
||||
this.backfill(consensusInfo, height, op, addr, voter);
|
||||
|
||||
this.pruneBlocks(consensusInfo);
|
||||
this.updateState({ consensusInfo });
|
||||
}
|
||||
|
||||
// Initializes the `ConsensusView` with empty objects.
|
||||
private initialiseConsensusView(
|
||||
consensusInfo: Types.ConsensusInfo,
|
||||
height: Types.BlockNumber,
|
||||
own: Types.Address,
|
||||
other: Types.Address
|
||||
) {
|
||||
const found = consensusInfo.find(([blockNumber]) => blockNumber === height);
|
||||
|
||||
let consensusView;
|
||||
if (found) {
|
||||
[, consensusView] = found;
|
||||
this.initialiseConsensusViewByRef(consensusView, own, other);
|
||||
} else {
|
||||
consensusView = {} as Types.ConsensusView;
|
||||
|
||||
this.initialiseConsensusViewByRef(consensusView, own, other);
|
||||
|
||||
const item: Types.ConsensusItem = [height, consensusView];
|
||||
const insertPos = consensusInfo.findIndex(
|
||||
([elHeight]) => elHeight < height
|
||||
);
|
||||
if (insertPos >= 0) {
|
||||
consensusInfo.splice(insertPos, 0, item);
|
||||
} else {
|
||||
consensusInfo.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the `ConsensusView` with empty objects.
|
||||
private initialiseConsensusViewByRef(
|
||||
consensusView: Types.ConsensusView,
|
||||
own: Types.Address,
|
||||
other: Types.Address
|
||||
) {
|
||||
if (!consensusView[own]) {
|
||||
consensusView[own] = {} as Types.ConsensusState;
|
||||
}
|
||||
|
||||
if (!consensusView[own][other]) {
|
||||
consensusView[own][other] = {} as Types.ConsensusDetail;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the block cache back from the `to` number to the last block.
|
||||
// The function `f` is used to evaluate if we should continue backfilling.
|
||||
// `f` returns false when backfilling the cache should be stopped, true to continue.
|
||||
//
|
||||
// Returns block number until which we backfilled.
|
||||
private backfill(
|
||||
consensusInfo: Types.ConsensusInfo,
|
||||
start: Types.BlockNumber,
|
||||
f: (i: Types.BlockNumber, index: number) => boolean,
|
||||
own: Types.Address,
|
||||
other: Types.Address
|
||||
) {
|
||||
// if this is the first block then we don't fill latter blocks
|
||||
// if there is only one block, then it also doesn't make
|
||||
// sense to backfill, because we could potentially backfill
|
||||
// until 0 (which could be unfortunate if the first received
|
||||
// block is e.g. 28317.
|
||||
if (consensusInfo.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let firstBlockNumber = consensusInfo[consensusInfo.length - 1][0];
|
||||
const limit = this.getState().best - BLOCKS_LIMIT;
|
||||
if (firstBlockNumber < limit) {
|
||||
firstBlockNumber = limit as Types.BlockNumber;
|
||||
}
|
||||
|
||||
if (start - 1 < firstBlockNumber) {
|
||||
// if the first block which would be backfilled is already
|
||||
// less than the first block number we can abort.
|
||||
//
|
||||
// this can happen if e.g. one authority is hanging behind,
|
||||
// most of them could e.g. be at 3000 and one is hanging behind
|
||||
// and sending info for 2000. then we can't start backfilling
|
||||
// from 2000.
|
||||
return;
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
while (start-- > 0) {
|
||||
counter++;
|
||||
if (counter >= BLOCKS_LIMIT) {
|
||||
break;
|
||||
}
|
||||
|
||||
const startBlockNumber = start as Types.BlockNumber;
|
||||
this.initialiseConsensusView(consensusInfo, startBlockNumber, own, other);
|
||||
const index = consensusInfo.findIndex(
|
||||
([blockNumber]) => blockNumber === start
|
||||
);
|
||||
const cont = f(start, index);
|
||||
if (!cont) {
|
||||
break;
|
||||
}
|
||||
|
||||
// we don't want to fill into nirvana
|
||||
const firstBlockReached = startBlockNumber <= firstBlockNumber;
|
||||
if (firstBlockReached) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateConsensusInfo(
|
||||
consensusInfo: Types.ConsensusInfo,
|
||||
height: Types.BlockNumber,
|
||||
addr: Types.Address,
|
||||
voter: Types.Address,
|
||||
data: Partial<Types.ConsensusDetail>
|
||||
) {
|
||||
const found = consensusInfo.findIndex(
|
||||
([blockNumber]) => blockNumber === height
|
||||
);
|
||||
if (found < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const k in data) {
|
||||
if (data.hasOwnProperty(k)) {
|
||||
consensusInfo[found][1][addr][voter][k] = data[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private pruneBlocks(consensusInfo: Types.ConsensusInfo) {
|
||||
if (consensusInfo.length >= BLOCKS_LIMIT) {
|
||||
consensusInfo.length = BLOCKS_LIMIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user