Loaded Rotated Keys for consumer chain

This commit is contained in:
liangping 2023-09-01 11:40:51 +08:00
parent 62a2d8f927
commit 1e3eb6c56e
6 changed files with 114 additions and 27 deletions

View File

@ -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,
},
}; };

View File

@ -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});
}
} }

View File

@ -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 {

View File

@ -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}`);
} }

View File

@ -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

View File

@ -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
} }
} }