improve update performance
This commit is contained in:
parent
86a5e9c0da
commit
0b8d4dc987
@ -1,51 +1,14 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Commit } from '@/types';
|
import type { PropType } from 'vue';
|
||||||
import { computed, type PropType } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
blocks: { type: Array as PropType<Commit[]> },
|
blocks: { type: Array as PropType<{height:string, color: string}[]> },
|
||||||
validator: { type: String },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const bars = computed(() => {
|
|
||||||
const uptime = Array(50).fill({ height: 0, color: 'bg-secondary' });
|
|
||||||
if(!props.blocks) return uptime
|
|
||||||
props.blocks.forEach((element) => {
|
|
||||||
// @deprecated
|
|
||||||
// const has = element.signatures?.findIndex(
|
|
||||||
// (sig) => sig.validator_address === props.validator
|
|
||||||
// );
|
|
||||||
// // console.log(has, props.validato, element)
|
|
||||||
// uptime.push({
|
|
||||||
// height: element.height,
|
|
||||||
// color: has > -1 ? 'bg-green-500' : 'bg-red-500',
|
|
||||||
// });
|
|
||||||
|
|
||||||
// show flag nil
|
|
||||||
const signature = element.signatures?.find(
|
|
||||||
(sig) => sig.validator_address === props.validator
|
|
||||||
);
|
|
||||||
// console.log("sign", signature)
|
|
||||||
let color = `bg-red-500`
|
|
||||||
if(signature) {
|
|
||||||
if(signature.block_id_flag === 'BLOCK_ID_FLAG_COMMIT') {
|
|
||||||
color = `bg-green-500`
|
|
||||||
} else {
|
|
||||||
color = `bg-yellow-500`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uptime.push({
|
|
||||||
height: element.height,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
uptime.shift();
|
|
||||||
});
|
|
||||||
return uptime;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex gap-0.5">
|
<div class="flex gap-0.5">
|
||||||
<div class="cursor-default" v-for="(item, index) in bars" :key="index">
|
<div class="cursor-default" v-for="(item, index) in blocks" :key="index">
|
||||||
<div class="tooltip"
|
<div class="tooltip"
|
||||||
:data-tip="item.height"
|
:data-tip="item.height"
|
||||||
:class="item.color"
|
:class="item.color"
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
import { ref, onMounted, computed, onUnmounted } from 'vue';
|
import { ref, onMounted, computed, onUnmounted } from 'vue';
|
||||||
import { fromHex, toBase64 } from '@cosmjs/encoding';
|
import { fromHex, toBase64 } from '@cosmjs/encoding';
|
||||||
import {
|
import {
|
||||||
useFormatter,
|
|
||||||
useStakingStore,
|
useStakingStore,
|
||||||
useBaseStore,
|
useBaseStore,
|
||||||
useBlockchain,
|
useBlockchain,
|
||||||
|
useFormatter,
|
||||||
} from '@/stores';
|
} from '@/stores';
|
||||||
import UptimeBar from '@/components/UptimeBar.vue';
|
import UptimeBar from '@/components/UptimeBar.vue';
|
||||||
import type { Commit, SlashingParam, SigningInfo } from '@/types';
|
import type { SlashingParam, SigningInfo } from '@/types';
|
||||||
import { consensusPubkeyToHexAddress, pubKeyToValcons, valconsToBase64 } from '@/libs';
|
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
|
||||||
|
|
||||||
const props = defineProps(['chain']);
|
const props = defineProps(['chain']);
|
||||||
|
|
||||||
@ -18,64 +18,87 @@ const format = useFormatter();
|
|||||||
const baseStore = useBaseStore();
|
const baseStore = useBaseStore();
|
||||||
const chainStore = useBlockchain();
|
const chainStore = useBlockchain();
|
||||||
const latest = ref(0);
|
const latest = ref(0);
|
||||||
const commits = ref([] as Commit[]);
|
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
const live = ref(true);
|
const live = ref(true);
|
||||||
const slashingParam = ref({} as SlashingParam);
|
const slashingParam = ref({} as SlashingParam);
|
||||||
|
|
||||||
const signingInfo = ref({} as Record<string, SigningInfo>);
|
const signingInfo = ref({} as Record<string, SigningInfo>);
|
||||||
|
|
||||||
// filter validators by keywords
|
interface BlockColor {
|
||||||
const validators = computed(() => {
|
height: string;
|
||||||
if (keyword)
|
color: string;
|
||||||
return stakingStore.validators.filter(
|
}
|
||||||
(x) => x.description.moniker.indexOf(keyword.value) > -1
|
interface ValidatorUnit {
|
||||||
);
|
moniker: string;
|
||||||
return stakingStore.validators;
|
blocks: BlockColor[];
|
||||||
|
hex: string;
|
||||||
|
base64: string;
|
||||||
|
missed_blocks_counter: number | string;
|
||||||
|
uptime: number;
|
||||||
|
signing: SigningInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function padding(blocks: BlockColor[] = []) {
|
||||||
|
const raw = Array(50).fill({ height: "0", color: 'bg-secondary' } as BlockColor).concat(blocks)
|
||||||
|
return raw.slice(raw.length - 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatorSet = computed(() => {
|
||||||
|
return stakingStore.validators.map((v) => {
|
||||||
|
const hex = consensusPubkeyToHexAddress(v.consensus_pubkey)
|
||||||
|
return {
|
||||||
|
moniker: v.description.moniker,
|
||||||
|
hex,
|
||||||
|
base64: toBase64(fromHex(hex))
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const list = computed(() => {
|
const blocks = ref({} as Record<string, BlockColor[]>);
|
||||||
if(chainStore.isConsumerChain) {
|
|
||||||
stakingStore.loadKeyRotationFromLocalstorage(baseStore.latest?.block?.header?.chain_id)
|
|
||||||
|
|
||||||
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
const grid = computed(() => {
|
||||||
const vset = validators.value.map((v) => {
|
|
||||||
|
const validators = keyword.value.length === 0 ? validatorSet.value :
|
||||||
const hexAddress = stakingStore.findRotatedHexAddress(v.consensus_pubkey)
|
validatorSet.value.filter((v) => v.moniker.toLowerCase().includes(keyword.value.toLowerCase()));
|
||||||
const signing =
|
|
||||||
signingInfo.value[hexAddress];
|
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
||||||
return {
|
return validators.map((v) => {
|
||||||
v,
|
const signing = signingInfo.value[v.hex];
|
||||||
signing,
|
const uptime = signing && window > 0
|
||||||
hex: toBase64(fromHex(hexAddress)),
|
|
||||||
uptime:
|
|
||||||
signing && window > 0
|
|
||||||
? (window - Number(signing.missed_blocks_counter)) / window
|
? (window - Number(signing.missed_blocks_counter)) / window
|
||||||
: undefined,
|
: undefined
|
||||||
};
|
return {
|
||||||
|
moniker: v.moniker,
|
||||||
|
hex: v.hex,
|
||||||
|
base64: v.base64,
|
||||||
|
blocks: padding(blocks.value[v.base64] || []) ,
|
||||||
|
uptime,
|
||||||
|
missed_blocks_counter: signing?.missed_blocks_counter || 0,
|
||||||
|
signing
|
||||||
|
} as ValidatorUnit;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
baseStore.$subscribe((_, state) => {
|
||||||
|
|
||||||
|
if(Number(state.latest.block.header.height) % 7 === 0 ) updateTotalSigningInfo();
|
||||||
|
|
||||||
|
state.latest.block.last_commit?.signatures?.forEach((s) => {
|
||||||
|
const block = blocks.value[s.validator_address] || [];
|
||||||
|
block.push({
|
||||||
|
height: state.latest.block.header.height,
|
||||||
|
color: s.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' :
|
||||||
|
s.block_id_flag === 'BLOCK_ID_FLAG_NIL' ? 'bg-yellow-500' : 'bg-red-500',
|
||||||
});
|
});
|
||||||
return vset;
|
if (block.length > 50) {
|
||||||
} else {
|
block.shift();
|
||||||
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
}
|
||||||
const vset = validators.value.map((v) => {
|
blocks.value[s.validator_address] = block;
|
||||||
const signing =
|
});
|
||||||
signingInfo.value[consensusPubkeyToHexAddress(v.consensus_pubkey)];
|
|
||||||
return {
|
|
||||||
v,
|
|
||||||
signing,
|
|
||||||
hex: toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey))),
|
|
||||||
uptime:
|
|
||||||
signing && window > 0
|
|
||||||
? (window - Number(signing.missed_blocks_counter)) / window
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return vset;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
live.value = true;
|
live.value = true;
|
||||||
|
|
||||||
baseStore.fetchLatest().then((l) => {
|
baseStore.fetchLatest().then((l) => {
|
||||||
let b = l;
|
let b = l;
|
||||||
if (
|
if (
|
||||||
@ -85,7 +108,6 @@ onMounted(() => {
|
|||||||
b = baseStore.recents?.at(0) || l;
|
b = baseStore.recents?.at(0) || l;
|
||||||
}
|
}
|
||||||
latest.value = Number(b.block.header.height);
|
latest.value = Number(b.block.header.height);
|
||||||
commits.value.unshift(b.block.last_commit);
|
|
||||||
const height = Number(b.block.header?.height || 0);
|
const height = Number(b.block.header?.height || 0);
|
||||||
if (height > 50) {
|
if (height > 50) {
|
||||||
// constructs sequence for loading blocks
|
// constructs sequence for loading blocks
|
||||||
@ -94,10 +116,18 @@ onMounted(() => {
|
|||||||
promise = promise.then(
|
promise = promise.then(
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
if (live.value && commits2.value.length < 50) {
|
if (live.value) {
|
||||||
// continue only if the page is living
|
// continue only if the page is living
|
||||||
baseStore.fetchBlock(i).then((x) => {
|
baseStore.fetchBlock(i).then((x) => {
|
||||||
commits.value.unshift(x.block.last_commit);
|
x.block.last_commit?.signatures?.forEach((s) => {
|
||||||
|
const block = blocks.value[s.validator_address] || [];
|
||||||
|
block.unshift({
|
||||||
|
height: x.block.header.height,
|
||||||
|
color: s.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' :
|
||||||
|
s.block_id_flag === 'BLOCK_ID_FLAG_NIL' ? 'bg-yellow-500' : 'bg-red-500',
|
||||||
|
});
|
||||||
|
blocks.value[s.validator_address] = block;
|
||||||
|
});
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -122,16 +152,6 @@ function updateTotalSigningInfo() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const commits2 = computed(() => {
|
|
||||||
const la = baseStore.recents.map((b) => b.block.last_commit);
|
|
||||||
// trigger update total signing info
|
|
||||||
if(la.length > 1 && Number(la.at(la.length-1)?.height|| 0) % 10 === 7) {
|
|
||||||
updateTotalSigningInfo();
|
|
||||||
};
|
|
||||||
const all = [...commits.value, ...la];
|
|
||||||
return all.length > 50 ? all.slice(all.length - 50) : all;
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
live.value = false;
|
live.value = false;
|
||||||
});
|
});
|
||||||
@ -185,27 +205,27 @@ function fetchAllKeyRotation() {
|
|||||||
<!-- 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 -->
|
||||||
<div :class="tab === '2' ? '' : 'hidden'">
|
<div :class="tab === '2' ? '' : 'hidden'">
|
||||||
<div class="flex flex-row flex-wrap gap-x-4 mt-4 justify-center">
|
<div class="flex flex-row flex-wrap gap-x-4 mt-4 justify-center">
|
||||||
<div v-for="({ v, signing, hex }, i) in list" :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 }}.{{ v.description.moniker }}</span
|
>{{ i + 1 }}.{{ unit.moniker }}</span
|
||||||
>
|
>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
v-if="Number(signing?.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"
|
||||||
>
|
>
|
||||||
{{ signing?.missed_blocks_counter }}
|
{{ unit?.missed_blocks_counter }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="badge badge-sm bg-transparent text-green-600 border-0 font-bold"
|
class="badge badge-sm bg-transparent text-green-600 border-0 font-bold"
|
||||||
>
|
>
|
||||||
{{ signing?.missed_blocks_counter }}
|
{{ unit?.missed_blocks_counter }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<UptimeBar :blocks="commits2" :validator="hex" />
|
<UptimeBar :blocks="unit.blocks" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5 text-xs flex justify-center gap-2">
|
<div class="mt-5 text-xs flex justify-center gap-2">
|
||||||
@ -228,53 +248,51 @@ function fetchAllKeyRotation() {
|
|||||||
<td>{{ $t('uptime.tombstoned') }}</td>
|
<td>{{ $t('uptime.tombstoned') }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr v-for="({ v, signing, uptime }, i) in list" class="hover">
|
<tr v-for="(v, i) in grid" class="hover">
|
||||||
<td>
|
<td>
|
||||||
<div class="truncate max-w-sm">
|
<div class="truncate max-w-sm">
|
||||||
{{ i + 1 }}. {{ v.description.moniker }}
|
{{ i + 1 }}. {{ v.moniker }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<span
|
<span
|
||||||
v-if="signing"
|
|
||||||
class=""
|
|
||||||
:class="
|
:class="
|
||||||
uptime && uptime > 0.95 ? 'text-green-500' : 'text-red-500'
|
v.uptime && v.uptime > 0.95 ? 'text-green-500' : 'text-red-500'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
:data-tip="`${signing.missed_blocks_counter} missing blocks`"
|
:data-tip="`${v.missed_blocks_counter} missing blocks`"
|
||||||
>
|
>
|
||||||
{{ format.percent(uptime) }}
|
{{ format.percent(v.uptime) }}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span v-if="signing && !signing.jailed_until.startsWith('1970')">
|
<span v-if="v.signing && !v.signing.jailed_until.startsWith('1970')">
|
||||||
<div
|
<div
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
:data-tip="format.toDay(signing?.jailed_until, 'long')"
|
:data-tip="format.toDay(v.signing.jailed_until, 'long')"
|
||||||
>
|
>
|
||||||
<span>{{ format.toDay(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="signing && signing.jailed_until.startsWith('1970')"
|
v-if="v.signing.jailed_until.startsWith('1970')"
|
||||||
class="text-right"
|
class="text-right"
|
||||||
>{{
|
>{{
|
||||||
format.percent(
|
format.percent(
|
||||||
Number(signing.index_offset) /
|
Number(v.signing.index_offset) /
|
||||||
(latest - Number(signing.start_height))
|
(latest - Number(v.signing.start_height))
|
||||||
)
|
)
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
{{ signing?.index_offset }}
|
{{ v.signing?.index_offset }}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">{{ signing?.start_height }}</td>
|
<td class="text-right">{{ v.signing?.start_height }}</td>
|
||||||
<td class="capitalize">{{ signing?.tombstoned }}</td>
|
<td class="capitalize">{{ v.signing?.tombstoned }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user