optimized block loading
This commit is contained in:
parent
727a53177e
commit
4a375f58d0
@ -5,10 +5,10 @@ import {
|
|||||||
useStakingStore,
|
useStakingStore,
|
||||||
useBaseStore,
|
useBaseStore,
|
||||||
useBlockchain,
|
useBlockchain,
|
||||||
useFormatter,
|
useFormatter,
|
||||||
} from '@/stores';
|
} from '@/stores';
|
||||||
import UptimeBar from '@/components/UptimeBar.vue';
|
import UptimeBar from '@/components/UptimeBar.vue';
|
||||||
import type { SlashingParam, SigningInfo } from '@/types';
|
import type { SlashingParam, SigningInfo, Block } from '@/types';
|
||||||
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
|
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
|
||||||
|
|
||||||
const props = defineProps(['chain']);
|
const props = defineProps(['chain']);
|
||||||
@ -53,24 +53,24 @@ const validatorSet = computed(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const blocks = ref({} as Record<string, BlockColor[]>);
|
const blockColors = ref({} as Record<string, BlockColor[]>);
|
||||||
|
|
||||||
const grid = computed(() => {
|
const grid = computed(() => {
|
||||||
|
|
||||||
const validators = keyword.value.length === 0 ? validatorSet.value :
|
const validators = keyword.value.length === 0 ? validatorSet.value :
|
||||||
validatorSet.value.filter((v) => v.moniker.toLowerCase().includes(keyword.value.toLowerCase()));
|
validatorSet.value.filter((v) => v.moniker.toLowerCase().includes(keyword.value.toLowerCase()));
|
||||||
|
|
||||||
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
||||||
return validators.map((v) => {
|
return validators.map((v) => {
|
||||||
const signing = signingInfo.value[v.hex];
|
const signing = signingInfo.value[v.hex];
|
||||||
const uptime = signing && window > 0
|
const uptime = signing && window > 0
|
||||||
? (window - Number(signing.missed_blocks_counter)) / window
|
? (window - Number(signing.missed_blocks_counter)) / window
|
||||||
: undefined
|
: undefined
|
||||||
return {
|
return {
|
||||||
moniker: v.moniker,
|
moniker: v.moniker,
|
||||||
hex: v.hex,
|
hex: v.hex,
|
||||||
base64: v.base64,
|
base64: v.base64,
|
||||||
blocks: padding(blocks.value[v.base64] || []) ,
|
blocks: padding(blockColors.value[v.base64] || []),
|
||||||
uptime,
|
uptime,
|
||||||
missed_blocks_counter: signing?.missed_blocks_counter || 0,
|
missed_blocks_counter: signing?.missed_blocks_counter || 0,
|
||||||
signing
|
signing
|
||||||
@ -78,77 +78,29 @@ const grid = computed(() => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const preload = ref(false);
|
||||||
baseStore.$subscribe((_, state) => {
|
baseStore.$subscribe((_, state) => {
|
||||||
const newHeight = Number(state.latest?.block?.header?.height || 0)
|
const newHeight = Number(state.latest?.block?.header?.height || 0)
|
||||||
if (newHeight > latest.value) {
|
if (newHeight > latest.value) {
|
||||||
latest.value = newHeight;
|
latest.value = newHeight;
|
||||||
if(Number(state.latest.block.header.height) % 7 === 0 ) updateTotalSigningInfo();
|
// initialize if it's the first time
|
||||||
validatorSet.value.forEach((v) => {
|
if(!preload.value) {
|
||||||
const sig = state.latest.block.last_commit?.signatures.find((s) => s.validator_address === v.base64)
|
preFill();
|
||||||
const block = blocks.value[v.base64] || [];
|
preload.value = true;
|
||||||
if (sig){
|
}
|
||||||
block.push({
|
|
||||||
height: state.latest.block.header.height,
|
if (Number(state.latest.block.header.height) % 7 === 0) updateTotalSigningInfo();
|
||||||
color: sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' : 'bg-yellow-500'
|
fillblock(state.latest);
|
||||||
});
|
|
||||||
} else {
|
|
||||||
block.push({
|
|
||||||
height: state.latest.block.header.height,
|
|
||||||
color: 'bg-red-500'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (block.length > 50) block.shift();
|
|
||||||
blocks.value[v.base64] = block;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
live.value = true;
|
live.value = true;
|
||||||
|
|
||||||
baseStore.fetchLatest().then((l) => {
|
|
||||||
let b = l;
|
// fill the recent blocks
|
||||||
if (
|
baseStore.recents?.forEach((b) => {
|
||||||
baseStore.recents?.findIndex((x) => x.block_id.hash === l.block_id.hash) >
|
fillblock(b, 'start');
|
||||||
-1
|
|
||||||
) {
|
|
||||||
b = baseStore.recents?.at(0) || l;
|
|
||||||
}
|
|
||||||
latest.value = Number(b.block.header.height);
|
|
||||||
const height = Number(b.block.header?.height || 0);
|
|
||||||
if (height > 50) {
|
|
||||||
// constructs sequence for loading blocks
|
|
||||||
let promise = Promise.resolve();
|
|
||||||
for (let i = height - 1; i > height - 50; i -= 1) {
|
|
||||||
promise = promise.then(
|
|
||||||
() =>
|
|
||||||
new Promise((resolve) => {
|
|
||||||
if (live.value) {
|
|
||||||
// continue only if the page is living
|
|
||||||
baseStore.fetchBlock(i).then((x) => {
|
|
||||||
validatorSet.value.forEach((v) => {
|
|
||||||
const sig = x.block.last_commit?.signatures.find((s) => s.validator_address === v.base64)
|
|
||||||
const block = blocks.value[v.base64] || [];
|
|
||||||
if (sig){
|
|
||||||
block.unshift({
|
|
||||||
height: x.block.header.height,
|
|
||||||
color: sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' : 'bg-yellow-500'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
block.unshift({
|
|
||||||
height: x.block.header.height,
|
|
||||||
color: 'bg-red-500'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
blocks.value[v.base64] = block;
|
|
||||||
});
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updateTotalSigningInfo();
|
updateTotalSigningInfo();
|
||||||
@ -158,6 +110,50 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function preFill() {
|
||||||
|
|
||||||
|
if(latest.value > 50 && baseStore.recents.length >= 49 ) return
|
||||||
|
// preload 50 blocks if recent blocks are not enough
|
||||||
|
let promise = Promise.resolve();
|
||||||
|
for (let i = latest.value - baseStore.recents.length; i > latest.value - 50 && i > 1; i -= 1) {
|
||||||
|
promise = promise.then(() =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
if (live.value) {
|
||||||
|
// continue only if the page is living
|
||||||
|
if (i > latest.value - 50)
|
||||||
|
baseStore.fetchBlock(i).then((x) => {
|
||||||
|
fillblock(x, 'start');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function fillblock(b: Block, direction: string = 'end') {
|
||||||
|
validatorSet.value.forEach((v) => {
|
||||||
|
const sig = b.block.last_commit?.signatures.find((s) => s.validator_address === v.base64)
|
||||||
|
const block = blockColors.value[v.base64] || [];
|
||||||
|
let color = {
|
||||||
|
height: b.block.header.height,
|
||||||
|
color: 'bg-red-500'
|
||||||
|
}
|
||||||
|
if (sig) {
|
||||||
|
color = {
|
||||||
|
height: b.block.header.height,
|
||||||
|
color: sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' : 'bg-yellow-500'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction === 'end') {
|
||||||
|
block.push(color);
|
||||||
|
} else {
|
||||||
|
block.unshift(color);
|
||||||
|
}
|
||||||
|
if (block.length > 50) block.shift();
|
||||||
|
blockColors.value[v.base64] = block;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateTotalSigningInfo() {
|
function updateTotalSigningInfo() {
|
||||||
chainStore.rpc.getSlashingSigningInfos().then((x) => {
|
chainStore.rpc.getSlashingSigningInfos().then((x) => {
|
||||||
x.info?.forEach((i) => {
|
x.info?.forEach((i) => {
|
||||||
@ -184,36 +180,24 @@ function fetchAllKeyRotation() {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||||
<a
|
<a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '3' }" @click="changeTab('3')">{{
|
||||||
class="tab text-gray-400 capitalize"
|
$t('uptime.overall') }}</a>
|
||||||
:class="{ 'tab-active': tab === '3' }"
|
<a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '2' }" @click="changeTab('2')">{{
|
||||||
@click="changeTab('3')"
|
$t('module.blocks') }}</a>
|
||||||
>{{ $t('uptime.overall') }}</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="tab text-gray-400 capitalize"
|
|
||||||
:class="{ 'tab-active': tab === '2' }"
|
|
||||||
@click="changeTab('2')"
|
|
||||||
>{{ $t('module.blocks') }}</a
|
|
||||||
>
|
|
||||||
<RouterLink :to="`/${chain}/uptime/customize`">
|
<RouterLink :to="`/${chain}/uptime/customize`">
|
||||||
<a class="tab text-gray-400 capitalize">{{ $t('uptime.customize') }}</a>
|
<a class="tab text-gray-400 capitalize">{{ $t('uptime.customize') }}</a>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-base-100 px-5 pt-5">
|
<div class="bg-base-100 px-5 pt-5">
|
||||||
<div class="flex items-center gap-x-4">
|
<div class="flex items-center gap-x-4">
|
||||||
<input
|
<input type="text" v-model="keyword" placeholder="Keywords to filter validators"
|
||||||
type="text"
|
class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600" />
|
||||||
v-model="keyword"
|
<button v-if="chainStore.isConsumerChain" class="btn btn-sm btn-primary" @click="fetchAllKeyRotation">Load
|
||||||
placeholder="Keywords to filter validators"
|
Rotated Keys</button>
|
||||||
class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600"
|
|
||||||
/>
|
|
||||||
<button v-if="chainStore.isConsumerChain" class="btn btn-sm btn-primary" @click="fetchAllKeyRotation">Load Rotated Keys</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="chainStore.isConsumerChain && Object.keys(stakingStore.keyRotation).length === 0"
|
<div v-if="chainStore.isConsumerChain && Object.keys(stakingStore.keyRotation).length === 0"
|
||||||
class="alert alert-warning my-4"
|
class="alert alert-warning my-4">
|
||||||
>
|
|
||||||
Note: Please load rotated keys to see the correct uptime
|
Note: Please load rotated keys to see the correct uptime
|
||||||
</div>
|
</div>
|
||||||
<!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 -->
|
<!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 -->
|
||||||
@ -222,20 +206,13 @@ function fetchAllKeyRotation() {
|
|||||||
<div v-for="(unit, i) in grid" :key="i">
|
<div v-for="(unit, i) in grid" :key="i">
|
||||||
<div class="flex justify-between py-0 w-[248px]">
|
<div class="flex justify-between py-0 w-[248px]">
|
||||||
<label class="truncate text-sm">
|
<label class="truncate text-sm">
|
||||||
<span class="ml-1 text-black dark:text-white"
|
<span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ unit.moniker }}</span>
|
||||||
>{{ i + 1 }}.{{ unit.moniker }}</span
|
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div v-if="Number(unit?.missed_blocks_counter || 0) > 10"
|
||||||
v-if="Number(unit?.missed_blocks_counter || 0) > 10"
|
class="badge badge-sm bg-transparent border-0 text-red-500 font-bold">
|
||||||
class="badge badge-sm bg-transparent border-0 text-red-500 font-bold"
|
|
||||||
>
|
|
||||||
{{ unit?.missed_blocks_counter }}
|
{{ unit?.missed_blocks_counter }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-else class="badge badge-sm bg-transparent text-green-600 border-0 font-bold">
|
||||||
v-else
|
|
||||||
class="badge badge-sm bg-transparent text-green-600 border-0 font-bold"
|
|
||||||
>
|
|
||||||
{{ unit?.missed_blocks_counter }}
|
{{ unit?.missed_blocks_counter }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -244,9 +221,9 @@ function fetchAllKeyRotation() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-5 text-xs flex justify-center gap-2">
|
<div class="mt-5 text-xs flex justify-center gap-2">
|
||||||
<span class=" font-bold">{{ $t('uptime.legend') }}: </span>
|
<span class=" font-bold">{{ $t('uptime.legend') }}: </span>
|
||||||
<span class="bg-green-500"> </span> {{ $t('uptime.committed')}}
|
<span class="bg-green-500"> </span> {{ $t('uptime.committed') }}
|
||||||
<span class="bg-yellow-500"> </span> {{ $t('uptime.precommitted')}}
|
<span class="bg-yellow-500"> </span> {{ $t('uptime.precommitted') }}
|
||||||
<span class="bg-red-500"> </span> {{ $t('uptime.missed')}}
|
<span class="bg-red-500"> </span> {{ $t('uptime.missed') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -269,40 +246,27 @@ function fetchAllKeyRotation() {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<span
|
<span :class="v.uptime && v.uptime > 0.95 ? 'text-green-500' : 'text-red-500'
|
||||||
:class="
|
">
|
||||||
v.uptime && v.uptime > 0.95 ? 'text-green-500' : 'text-red-500'
|
<div class="tooltip" :data-tip="`${v.missed_blocks_counter} missing blocks`">
|
||||||
"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="tooltip"
|
|
||||||
:data-tip="`${v.missed_blocks_counter} missing blocks`"
|
|
||||||
>
|
|
||||||
{{ format.percent(v.uptime) }}
|
{{ format.percent(v.uptime) }}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span v-if="v.signing && !v.signing.jailed_until.startsWith('1970')">
|
<span v-if="v.signing && !v.signing.jailed_until.startsWith('1970')">
|
||||||
<div
|
<div class="tooltip" :data-tip="format.toDay(v.signing.jailed_until, 'long')">
|
||||||
class="tooltip"
|
|
||||||
:data-tip="format.toDay(v.signing.jailed_until, 'long')"
|
|
||||||
>
|
|
||||||
<span>{{ format.toDay(v.signing.jailed_until, 'from') }}</span>
|
<span>{{ format.toDay(v.signing.jailed_until, 'from') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-xs text-right">
|
<td class="text-xs text-right">
|
||||||
<span
|
<span v-if="v.signing && v.signing.jailed_until.startsWith('1970')" class="text-right">{{
|
||||||
v-if="v.signing && v.signing.jailed_until.startsWith('1970')"
|
format.percent(
|
||||||
class="text-right"
|
Number(v.signing.index_offset) /
|
||||||
>{{
|
(latest - Number(v.signing.start_height))
|
||||||
format.percent(
|
)
|
||||||
Number(v.signing.index_offset) /
|
}}</span>
|
||||||
(latest - Number(v.signing.start_height))
|
|
||||||
)
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
{{ v.signing?.index_offset }}
|
{{ v.signing?.index_offset }}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">{{ v.signing?.start_height }}</td>
|
<td class="text-right">{{ v.signing?.start_height }}</td>
|
||||||
@ -312,12 +276,10 @@ function fetchAllKeyRotation() {
|
|||||||
<tr>
|
<tr>
|
||||||
<td colspan="2" class="text-right">
|
<td colspan="2" class="text-right">
|
||||||
{{ $t('uptime.minimum_uptime') }}:
|
{{ $t('uptime.minimum_uptime') }}:
|
||||||
<span
|
<span class="lowercase tooltip" :data-tip="`Window size: ${slashingParam.signed_blocks_window}`"><span
|
||||||
class="lowercase tooltip"
|
class="ml-2 btn btn-error btn-xs">{{
|
||||||
:data-tip="`Window size: ${slashingParam.signed_blocks_window}`"
|
format.percent(slashingParam.min_signed_per_window)
|
||||||
><span class="ml-2 btn btn-error btn-xs">{{
|
}}</span>
|
||||||
format.percent(slashingParam.min_signed_per_window)
|
|
||||||
}}</span>
|
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td colspan="8"></td>
|
<td colspan="8"></td>
|
||||||
|
Loading…
Reference in New Issue
Block a user