forked from cerc-io/cosmos-explorer
Loaded Rotated Keys for consumer chain
This commit is contained in:
parent
62a2d8f927
commit
1e3eb6c56e
@ -190,4 +190,8 @@ export const DEFAULT: RequestRegistry = {
|
|||||||
url: '/ibc/core/connection/v1/connections/{connection_id}/client_state',
|
url: '/ibc/core/connection/v1/connections/{connection_id}/client_state',
|
||||||
adapter,
|
adapter,
|
||||||
},
|
},
|
||||||
|
interchain_security_ccv_provider_validator_consumer_addr: {
|
||||||
|
url: '/interchain_security/ccv/provider/validator_consumer_addr?provider_address={provider_address}&chain_id={chain_id}',
|
||||||
|
adapter,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -326,4 +326,7 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
|||||||
port_id,
|
port_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
async getInterchainSecurityValidatorRotatedKey(chain_id: string, provider_address: string) {
|
||||||
|
return this.request(this.registry.interchain_security_ccv_provider_validator_consumer_addr, {chain_id, provider_address});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,7 @@ export interface RequestRegistry extends AbstractRegistry {
|
|||||||
ibc_core_connection_connections: Request<PaginatedIBCConnections>;
|
ibc_core_connection_connections: Request<PaginatedIBCConnections>;
|
||||||
ibc_core_connection_connections_connection_id: Request<ConnectionWithProof>;
|
ibc_core_connection_connections_connection_id: Request<ConnectionWithProof>;
|
||||||
ibc_core_connection_connections_connection_id_client_state: Request<ClientStateWithProof>;
|
ibc_core_connection_connections_connection_id_client_state: Request<ClientStateWithProof>;
|
||||||
|
interchain_security_ccv_provider_validator_consumer_addr: Request<{consumer_address: string}>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function adapter<T>(source: any): T {
|
export function adapter<T>(source: any): T {
|
||||||
|
@ -93,9 +93,9 @@ async function onChange () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchPosition() {
|
async function fetchPosition() {
|
||||||
let dumpurl = rpc.value.replace('consensus_state', 'dump_consensus_state');
|
// let dumpurl = rpc.value.replace('consensus_state', 'dump_consensus_state');
|
||||||
try {
|
try {
|
||||||
const response = await fetch(dumpurl);
|
const response = await fetch(rpc.value);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error: ${response.status}`);
|
throw new Error(`HTTP error: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
} 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 { Commit, SlashingParam, SigningInfo } from '@/types';
|
||||||
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
|
import { consensusPubkeyToHexAddress, pubKeyToValcons, valconsToBase64 } from '@/libs';
|
||||||
|
|
||||||
const props = defineProps(['chain']);
|
const props = defineProps(['chain']);
|
||||||
|
|
||||||
@ -25,7 +25,6 @@ const slashingParam = ref({} as SlashingParam);
|
|||||||
|
|
||||||
const signingInfo = ref({} as Record<string, SigningInfo>);
|
const signingInfo = ref({} as Record<string, SigningInfo>);
|
||||||
|
|
||||||
const filterOptout = ref(false);
|
|
||||||
// filter validators by keywords
|
// filter validators by keywords
|
||||||
const validators = computed(() => {
|
const validators = computed(() => {
|
||||||
if (keyword)
|
if (keyword)
|
||||||
@ -36,6 +35,27 @@ const validators = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const list = computed(() => {
|
const list = computed(() => {
|
||||||
|
if(chainStore.isConsumerChain) {
|
||||||
|
stakingStore.loadKeyRotationFromLocalstorage(baseStore.latest?.block?.header?.chain_id)
|
||||||
|
|
||||||
|
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
||||||
|
const vset = validators.value.map((v) => {
|
||||||
|
|
||||||
|
const hexAddress = stakingStore.findRotatedHexAddress(v.consensus_pubkey)
|
||||||
|
const signing =
|
||||||
|
signingInfo.value[hexAddress];
|
||||||
|
return {
|
||||||
|
v,
|
||||||
|
signing,
|
||||||
|
hex: toBase64(fromHex(hexAddress)),
|
||||||
|
uptime:
|
||||||
|
signing && window > 0
|
||||||
|
? (window - Number(signing.missed_blocks_counter)) / window
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return vset;
|
||||||
|
} else {
|
||||||
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
||||||
const vset = validators.value.map((v) => {
|
const vset = validators.value.map((v) => {
|
||||||
const signing =
|
const signing =
|
||||||
@ -50,12 +70,12 @@ const list = computed(() => {
|
|||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return filterOptout.value ? vset.filter((x) => x.signing) : vset;
|
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 (
|
||||||
@ -113,6 +133,10 @@ const tab = ref('2');
|
|||||||
function changeTab(v: string) {
|
function changeTab(v: string) {
|
||||||
tab.value = v;
|
tab.value = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchAllKeyRotation() {
|
||||||
|
stakingStore.fetchAllKeyRotation(baseStore.latest?.block?.header?.chain_id)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -136,16 +160,19 @@ function changeTab(v: string) {
|
|||||||
</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">
|
||||||
<label v-if="chainStore.isConsumerChain" class="text-center">
|
|
||||||
<input type="checkbox" v-model="filterOptout" class="checkbox" />
|
|
||||||
{{ $t('uptime.only_consumer_set') }}
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
v-model="keyword"
|
v-model="keyword"
|
||||||
placeholder="Keywords to filter validators"
|
placeholder="Keywords to filter validators"
|
||||||
class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600"
|
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 v-if="chainStore.isConsumerChain && Object.keys(stakingStore.keyRotation).length === 0"
|
||||||
|
class="alert alert-warning my-4"
|
||||||
|
>
|
||||||
|
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 -->
|
||||||
<div
|
<div
|
||||||
|
@ -4,8 +4,9 @@ import { useBlockchain } from './useBlockchain';
|
|||||||
import { get } from '@/libs/http';
|
import { get } from '@/libs/http';
|
||||||
import type { StakingParam, StakingPool, Validator } from '@/types';
|
import type { StakingParam, StakingPool, Validator } from '@/types';
|
||||||
import { CosmosRestClient } from '@/libs/client';
|
import { CosmosRestClient } from '@/libs/client';
|
||||||
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
|
import { consensusPubkeyToHexAddress, pubKeyToValcons, valconsToBase64 } from '@/libs';
|
||||||
import { toHex, fromBase64 } from '@cosmjs/encoding';
|
import { toHex, fromBase64, toBase64, fromHex, fromBech32 } from '@cosmjs/encoding';
|
||||||
|
import { useBaseStore } from './useBaseStore';
|
||||||
|
|
||||||
export const useStakingStore = defineStore('stakingStore', {
|
export const useStakingStore = defineStore('stakingStore', {
|
||||||
state: () => {
|
state: () => {
|
||||||
@ -24,6 +25,7 @@ export const useStakingStore = defineStore('stakingStore', {
|
|||||||
bonded_tokens: string;
|
bonded_tokens: string;
|
||||||
not_bonded_tokens: string;
|
not_bonded_tokens: string;
|
||||||
},
|
},
|
||||||
|
keyRotation: {} as Record<string, string>,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
@ -78,11 +80,60 @@ export const useStakingStore = defineStore('stakingStore', {
|
|||||||
delegatorAddr
|
delegatorAddr
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
async fetchKeyRotation(chain_id: string, validatorAddr: string ) : Promise<string> {
|
||||||
|
if(this.blockchain.isConsumerChain) {
|
||||||
|
if(this.blockchain.current?.providerChain.api && this.blockchain.current.providerChain.api.length > 0) {
|
||||||
|
const signatures = useBaseStore().latest?.block?.last_commit.signatures
|
||||||
|
if(signatures) {
|
||||||
|
// console.log(signatures)
|
||||||
|
const key = toBase64(fromHex(valconsToBase64(validatorAddr)))
|
||||||
|
const exists = signatures.findIndex((x) => x.validator_address === key)
|
||||||
|
if(exists < 0) {
|
||||||
|
|
||||||
|
const client = CosmosRestClient.newDefault(this.blockchain.current.providerChain.api[0].address)
|
||||||
|
const res = await client.getInterchainSecurityValidatorRotatedKey(chain_id, validatorAddr);
|
||||||
|
if(res.consumer_address) {
|
||||||
|
this.keyRotation[validatorAddr] = res.consumer_address
|
||||||
|
localStorage.setItem(`key-rotation-${chain_id}`, JSON.stringify(this.keyRotation))
|
||||||
|
}
|
||||||
|
return res.consumer_address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadKeyRotationFromLocalstorage(chain_id: string) {
|
||||||
|
const keyRotation = localStorage.getItem(`key-rotation-${chain_id}`)
|
||||||
|
this.keyRotation = keyRotation ? JSON.parse(keyRotation) : {}
|
||||||
|
},
|
||||||
|
|
||||||
|
findRotatedHexAddress(key: {
|
||||||
|
"@type": string;
|
||||||
|
key: string;
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const prefix = "cosmos"
|
||||||
|
const conskey = pubKeyToValcons(key, prefix)
|
||||||
|
const rotated = this.keyRotation[conskey]
|
||||||
|
if(rotated) {
|
||||||
|
return valconsToBase64(rotated)
|
||||||
|
}
|
||||||
|
return consensusPubkeyToHexAddress(key)
|
||||||
|
|
||||||
|
},
|
||||||
|
async fetchAllKeyRotation(chain_id: string) {
|
||||||
|
for(const val of this.validators) {
|
||||||
|
const { prefix } = fromBech32(val.operator_address)
|
||||||
|
await this.fetchKeyRotation(chain_id, pubKeyToValcons(val.consensus_pubkey, prefix.replace('valoper','')))
|
||||||
|
}
|
||||||
|
},
|
||||||
async fetchValidators(status: string) {
|
async fetchValidators(status: string) {
|
||||||
if(this.blockchain.isConsumerChain) {
|
if(this.blockchain.isConsumerChain) {
|
||||||
if(this.blockchain.current?.providerChain.api && this.blockchain.current.providerChain.api.length > 0) {
|
if(this.blockchain.current?.providerChain.api && this.blockchain.current.providerChain.api.length > 0) {
|
||||||
const client = CosmosRestClient.newDefault(this.blockchain.current.providerChain.api[0].address)
|
const client = CosmosRestClient.newDefault(this.blockchain.current.providerChain.api[0].address)
|
||||||
// const vals = await client.getBaseValidatorsetLatest(0)
|
// provider validators
|
||||||
const res = await client.getStakingValidators(status)
|
const res = await client.getStakingValidators(status)
|
||||||
const proVals = res.validators.sort(
|
const proVals = res.validators.sort(
|
||||||
(a, b) => Number(b.delegator_shares) - Number(a.delegator_shares)
|
(a, b) => Number(b.delegator_shares) - Number(a.delegator_shares)
|
||||||
@ -90,6 +141,7 @@ export const useStakingStore = defineStore('stakingStore', {
|
|||||||
if (status === 'BOND_STATUS_BONDED') {
|
if (status === 'BOND_STATUS_BONDED') {
|
||||||
this.validators = proVals;
|
this.validators = proVals;
|
||||||
}
|
}
|
||||||
|
|
||||||
return proVals
|
return proVals
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user