add group
This commit is contained in:
parent
efbf2731e0
commit
43e403c061
@ -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"
|
|
||||||
> </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"
|
||||||
|
> </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)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user