add group

This commit is contained in:
liangping 2022-10-16 11:11:44 +08:00
parent efbf2731e0
commit 43e403c061

View File

@ -1,107 +1,163 @@
<template> <template>
<div class="px-0"> <div class="px-0">
<b-card> <b-tabs>
<b-alert <b-tab title="Group By Validator">
variant="danger" <b-card>
:show="syncing" <b-alert
> variant="danger"
<div class="alert-body"> :show="syncing"
<span>No new blocks have been produced since <strong>{{ latestTime }}</strong> </span> >
</div> <div class="alert-body">
</b-alert> <span>No new blocks have been produced since <strong>{{ latestTime }}</strong> </span>
<b-card </div>
no-body </b-alert>
class="mb-1" <b-card
> no-body
<b-button class="mb-1"
to="./uptime/my" >
variant="primary" <b-row>
> <b-col md="3">
Browse favorite only <h4>#{{ height }}</h4>
</b-button> </b-col>
<b-input-group> <b-col md="9">
<b-input-group-prepend is-text> <b-input-group>
<b-form-checkbox <b-input-group-prepend is-text>
v-model="missedFilter" <b-form-checkbox
v-b-tooltip.hover v-model="missedFilter"
title="Only missed blocks" v-b-tooltip.hover
name="viewMissed" title="Only missed blocks"
/> name="viewMissed"
</b-input-group-prepend> />
<b-form-input </b-input-group-prepend>
v-model="query" <b-form-input
placeholder="Keywords to filter validators" v-model="query"
/> placeholder="Keywords to filter validators"
</b-input-group> />
</b-card> <b-input-group-append>
<b-row> <b-button
<b-col to="./uptime/my"
v-for="(x,index) in uptime" variant="primary"
:key="index" >
sm="12" Favorite
md="4" </b-button>
xl="3" </b-input-group-append>
class="text-truncate" </b-input-group>
> </b-col>
<div class="d-flex justify-content-between"> </b-row>
<b-form-checkbox </b-card>
v-model="pinned" <b-row>
:value="`${chain}#${x.address}`" <b-col
class="custom-control-warning text-truncate" v-for="(x,index) in uptime"
@change="pinValidator(`${chain}#${x.address}`)" :key="index"
><span class="d-inline-block text-truncate font-weight-bold align-bottom">{{ index+1 }} {{ x.validator.moniker }}</span> sm="12"
</b-form-checkbox> md="4"
<span v-if="missing[x.address]"> xl="3"
<b-badge class="text-truncate"
v-if="missing[x.address].missed_blocks_counter > 0" >
v-b-tooltip.hover.v-danger <div class="d-flex justify-content-between">
variant="light-danger" <b-form-checkbox
:title="`${missing[x.address].missed_blocks_counter} missed blocks`" v-model="pinned"
class="text-danger text-bolder" :value="`${chain}#${x.address}`"
> class="custom-control-warning text-truncate"
{{ missing[x.address].missed_blocks_counter }} @change="pinValidator(`${chain}#${x.address}`)"
</b-badge> ><span class="d-inline-block text-truncate font-weight-bold align-bottom">{{ index+1 }} {{ x.validator.moniker }}</span>
<b-badge </b-form-checkbox>
v-else <span v-if="missing[x.address]">
v-b-tooltip.hover.v-success <b-badge
variant="light-success" v-if="missing[x.address].missed_blocks_counter > 0"
title="Perfect! No missed blocks" v-b-tooltip.hover.v-danger
> variant="light-danger"
0 :title="`${missing[x.address].missed_blocks_counter} missed blocks`"
</b-badge> class="text-danger text-bolder"
</span> >
</div> {{ missing[x.address].missed_blocks_counter }}
<b-skeleton-wrapper :loading="loading"> </b-badge>
<template #loading> <b-badge
<b-skeleton width="100%" /> v-else
</template> v-b-tooltip.hover.v-success
<template #default> variant="light-success"
<div class="d-flex justify-content-between align-self-stretch flex-wrap"> title="Perfect! No missed blocks"
<div >
v-for="(b,i) in blocks" 0
:key="i" </b-badge>
style="width:1.5%;" </span>
><router-link :to="`./blocks/${b.height}`">
<div
v-b-tooltip.hover.v-second
:title="b.height"
:class="b.sigs && b.sigs[x.address] ? b.sigs[x.address] : 'bg-light-success'"
class="m-auto"
>&nbsp;</div>
</router-link>
</div>
</div> </div>
</template> <b-skeleton-wrapper :loading="loading">
</b-skeleton-wrapper> <template #loading>
</b-col> <b-skeleton width="100%" />
</b-row> </template>
</b-card> <template #default>
<div class="d-flex justify-content-between align-self-stretch flex-wrap">
<div
v-for="(b,i) in blocks"
:key="i"
style="width:1.5%;"
><router-link :to="`./blocks/${b.height}`">
<div
v-b-tooltip.hover.v-second
:title="b.height"
:class="b.sigs && b.sigs[x.address] ? b.sigs[x.address] : 'bg-light-success'"
class="m-auto"
>&nbsp;</div>
</router-link>
</div>
</div>
</template>
</b-skeleton-wrapper>
</b-col>
</b-row>
</b-card>
</b-tab>
<b-tab title="Group By Proposer">
<b-card>
<b-row>
<b-col
md="3"
sm="12"
>
<h4>#{{ height }}</h4>
</b-col>
<b-col
sm="12"
md="9"
>
<b-input-group
prepend="Qty of absent validators"
>
<b-form-input
id="threshold"
v-model="threshold"
placeholder="Absent validator number in blocks"
/>
</b-input-group>
</b-col>
</b-row>
</b-card>
<b-row>
<b-col
v-for="(x, i) in proposerViewData"
:key="i"
md="4"
sm="12"
class="text-truncate"
>
{{ x.name }}
<b-badge :variant="x.counter > 0 ? 'light-danger': 'light-success'">
{{ x.counter }} / {{ x.proposed }}
</b-badge>
</b-col>
</b-row>
</b-tab>
</b-tabs>
</div> </div>
</template> </template>
<script> <script>
import { import {
BSkeleton, BSkeletonWrapper, BSkeleton, BSkeletonWrapper, BInputGroupAppend, BTabs, BTab, BFormGroup,
BRow, BCol, VBTooltip, BFormInput, BCard, BAlert, BFormCheckbox, BButton, BBadge, BInputGroup, BInputGroupPrepend, BRow, BCol, VBTooltip, BFormInput, BCard, BAlert, BFormCheckbox, BButton, BBadge, BInputGroup, BInputGroupPrepend,
} from 'bootstrap-vue' } from 'bootstrap-vue'
@ -115,6 +171,7 @@ export default {
BRow, BRow,
BCol, BCol,
BFormInput, BFormInput,
BFormGroup,
BCard, BCard,
BAlert, BAlert,
BButton, BButton,
@ -124,6 +181,9 @@ export default {
BSkeleton, BSkeleton,
BSkeletonWrapper, BSkeletonWrapper,
BInputGroupPrepend, BInputGroupPrepend,
BInputGroupAppend,
BTabs,
BTab,
}, },
directives: { directives: {
'b-tooltip': VBTooltip, 'b-tooltip': VBTooltip,
@ -132,6 +192,7 @@ export default {
const { chain } = this.$route.params const { chain } = this.$route.params
const pinned = localStorage.getItem('pinned') ? localStorage.getItem('pinned').split(',') : '' const pinned = localStorage.getItem('pinned') ? localStorage.getItem('pinned').split(',') : ''
return { return {
height: 0,
loading: true, loading: true,
missedFilter: false, missedFilter: false,
pinned, pinned,
@ -142,6 +203,11 @@ export default {
blocks: Array.from('0'.repeat(50)).map(x => ({ sigs: {}, height: Number(x) })), blocks: Array.from('0'.repeat(50)).map(x => ({ sigs: {}, height: Number(x) })),
syncing: false, syncing: false,
latestTime: '', latestTime: '',
threshold: 10,
proposers: {},
absentValsInBlock: {},
numOfBlock: 1000,
temp: 0,
} }
}, },
computed: { computed: {
@ -157,10 +223,30 @@ export default {
} }
return rets return rets
}, },
// Compose data for group by proposer
proposerViewData() {
const valCounter = {}
this.validators.forEach(x => {
valCounter[this.hex2base64(consensusPubkeyToHexAddress(x.consensus_pubkey))] = {
name: x.description.moniker,
counter: 0,
proposed: 0,
}
})
Object.keys(this.proposers).forEach(height => {
const num = this.absentValsInBlock[height] || 0
if (valCounter[this.proposers[height]]) {
if (num > this.threshold) {
valCounter[this.proposers[height]].counter += 1 // (num >= Number(this.threshold) ? 1 : 0)
}
valCounter[this.proposers[height]].proposed += 1
}
})
return Object.values(valCounter).sort((a, b) => b.counter - a.counter)
},
}, },
created() { created() {
const cached = JSON.parse(getCachedValidators(this.$route.params.chain)) const cached = JSON.parse(getCachedValidators(this.$route.params.chain))
if (cached) { if (cached) {
this.validators = cached this.validators = cached
} }
@ -216,7 +302,7 @@ export default {
blocks.push({ sigs, height }) blocks.push({ sigs, height })
this.blocks = blocks this.blocks = blocks
this.timer = setInterval(this.fetch_latest, 6000) this.timer = setInterval(this.fetch_latest, 1000)
this.loading = false this.loading = false
}) })
}, },
@ -231,16 +317,30 @@ export default {
return toBase64(fromHex(v)) return toBase64(fromHex(v))
}, },
fetch_status(height, resolve) { fetch_status(height, resolve) {
const block = this.blocks.find(b => b.height === height) this.$http.getBlockByHeight(height).then(res => {
if (block) { resolve()
this.$http.getBlockByHeight(height).then(res => { const block = this.blocks.find(b => b.height === height)
resolve() // update valiators states
if (block) {
const sigs = this.initColor() const sigs = this.initColor()
res.block.last_commit.signatures.forEach(x => { res.block.last_commit.signatures.forEach(x => {
if (x.validator_address) sigs[x.validator_address] = 'bg-success' if (x.validator_address) sigs[x.validator_address] = 'bg-success'
}) })
this.$set(block, 'sigs', sigs) this.$set(block, 'sigs', sigs)
}) }
// update proposer states
this.count(res.block)
})
},
/// count how many absent valiators in a block
count(block) {
const count = block.last_commit.signatures.reduce((p, c) => (c.block_id_flag !== 'BLOCK_ID_FLAG_COMMIT' ? p + 1 : p), 0)
// Notes: block.header.height == last_commint.height + 1
this.$set(this.proposers, block.header.height, block.header.proposer_address)
this.$set(this.absentValsInBlock, block.last_commit.height, count)
if (count >= this.threshold) {
this.temp += 1
} }
}, },
fetch_latest() { fetch_latest() {
@ -249,12 +349,14 @@ export default {
res.block.last_commit.signatures.forEach(x => { res.block.last_commit.signatures.forEach(x => {
if (x.validator_address) sigs[x.validator_address] = 'bg-success' if (x.validator_address) sigs[x.validator_address] = 'bg-success'
}) })
this.height = res.block.header.height
const block = this.blocks.find(b => b.height === res.block.last_commit.height) const block = this.blocks.find(b => b.height === res.block.last_commit.height)
if (typeof block === 'undefined') { // mei if (typeof block === 'undefined') { // mei
// this.$set(block, 0, typeof sigs !== 'undefined') // this.$set(block, 0, typeof sigs !== 'undefined')
if (this.blocks.length >= 50) this.blocks.shift() if (this.blocks.length >= 50) this.blocks.shift()
this.blocks.push({ sigs, height: res.block.last_commit.height }) this.blocks.push({ sigs, height: res.block.last_commit.height })
} }
this.count(res.block)
}) })
}, },
}, },