Skip to content

Commit

Permalink
Merge pull request #776 from oasisprotocol/lw/fallback-refresh
Browse files Browse the repository at this point in the history
Check stale validators with RPC
  • Loading branch information
lukaw3d committed Mar 28, 2022
2 parents 09cbc04 + c745647 commit 481a81d
Show file tree
Hide file tree
Showing 17 changed files with 912 additions and 525 deletions.
Expand Up @@ -76,14 +76,6 @@ exports[`<ValidatorList /> empty should match snapshot 1`] = `
}
}
@media screen and (max-width:599px) {
}
@media screen and (max-width:599px) {
}
<div
class="c0"
>
Expand Down Expand Up @@ -148,70 +140,70 @@ exports[`<ValidatorList /> list should match snapshot 1`] = `
stroke: none;
}
.c18 {
.c17 {
display: inline-block;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
width: 24px;
height: 24px;
fill: #FF4040;
stroke: #FF4040;
fill: #00C781;
stroke: #00C781;
}
.c18 g {
.c17 g {
fill: inherit;
stroke: inherit;
}
.c18 *:not([stroke])[fill="none"] {
.c17 *:not([stroke])[fill="none"] {
stroke-width: 0;
}
.c18 *[stroke*="#"],
.c18 *[STROKE*="#"] {
.c17 *[stroke*="#"],
.c17 *[STROKE*="#"] {
stroke: inherit;
fill: none;
}
.c18 *[fill-rule],
.c18 *[FILL-RULE],
.c18 *[fill*="#"],
.c18 *[FILL*="#"] {
.c17 *[fill-rule],
.c17 *[FILL-RULE],
.c17 *[fill*="#"],
.c17 *[FILL*="#"] {
fill: inherit;
stroke: none;
}
.c17 {
.c18 {
display: inline-block;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
width: 24px;
height: 24px;
fill: #00C781;
stroke: #00C781;
fill: #FF4040;
stroke: #FF4040;
}
.c17 g {
.c18 g {
fill: inherit;
stroke: inherit;
}
.c17 *:not([stroke])[fill="none"] {
.c18 *:not([stroke])[fill="none"] {
stroke-width: 0;
}
.c17 *[stroke*="#"],
.c17 *[STROKE*="#"] {
.c18 *[stroke*="#"],
.c18 *[STROKE*="#"] {
stroke: inherit;
fill: none;
}
.c17 *[fill-rule],
.c17 *[FILL-RULE],
.c17 *[fill*="#"],
.c17 *[FILL*="#"] {
.c18 *[fill-rule],
.c18 *[FILL-RULE],
.c18 *[fill*="#"],
.c18 *[FILL*="#"] {
fill: inherit;
stroke: none;
}
Expand Down
Expand Up @@ -11,6 +11,7 @@ import { ValidatorList } from '..'

const activeValidator: Validator = {
address: 'oasis1qpc4ze5zzq3aa5mu5ttu4ku4ctp5t6x0asemymfz',
nodeAddress: 'oasis1qpwrtzadlddfuvdhjlta85268ju7dj0flsrfgg5x',
current_rate: 0.1,
rank: 0,
status: 'active',
Expand All @@ -25,6 +26,7 @@ const activeValidator: Validator = {
}
const inactiveValidator: Validator = {
address: 'oasis1qzyqaxestzlum26e2vdgvkerm6d9qgdp7gh2pxqe',
nodeAddress: 'oasis1qrdlqcv3tnv7qzuucnq0fncua52n66x7n5pm3n93',
current_rate: 0.2,
rank: 1,
status: 'inactive',
Expand All @@ -34,6 +36,7 @@ const inactiveValidator: Validator = {
}
const unknownValidator: Validator = {
address: 'oasis1qrfe9n26nq3t6vc9hlu9gnupwf4rm6wr0uglh3r7',
nodeAddress: 'oasis1qq672rjh7mldhaj35mlf4w34m8jtl9vu2c8qspkz',
current_rate: 0.2,
rank: 2,
status: 'unknown',
Expand Down Expand Up @@ -69,19 +72,37 @@ describe('<ValidatorList />', () => {

it('empty should match snapshot', () => {
const component = renderComponent(store)
store.dispatch(stakingActions.updateValidators([]))
store.dispatch(
stakingActions.updateValidators({
timestamp: new Date('2022').getTime(),
network: 'mainnet',
list: [],
}),
)
expect(component.container.firstChild).toMatchSnapshot()
})

it('list should match snapshot', () => {
const component = renderComponent(store)
store.dispatch(stakingActions.updateValidators([activeValidator, inactiveValidator, unknownValidator]))
store.dispatch(
stakingActions.updateValidators({
timestamp: new Date('2022').getTime(),
network: 'mainnet',
list: [activeValidator, inactiveValidator, unknownValidator],
}),
)
expect(component.container.firstChild).toMatchSnapshot()
})

it('should display validator details on click', async () => {
renderComponent(store)
store.dispatch(stakingActions.updateValidators([activeValidator]))
store.dispatch(
stakingActions.updateValidators({
timestamp: new Date('2022').getTime(),
network: 'mainnet',
list: [activeValidator],
}),
)

let row = screen.getByText(/test-validator/)
expect(row).toBeVisible()
Expand All @@ -97,7 +118,13 @@ describe('<ValidatorList />', () => {

it('should only display the details of a single validator', async () => {
renderComponent(store)
store.dispatch(stakingActions.updateValidators([activeValidator, inactiveValidator]))
store.dispatch(
stakingActions.updateValidators({
timestamp: new Date('2022').getTime(),
network: 'mainnet',
list: [activeValidator, inactiveValidator],
}),
)

let row = screen.getByText(/test-validator1/)
expect(row).toBeVisible()
Expand Down
12 changes: 5 additions & 7 deletions src/app/pages/StakingPage/Features/ValidatorList/index.tsx
Expand Up @@ -111,13 +111,11 @@ export const ValidatorList = memo((props: Props) => {
{t('common.validators', 'Validators')}
{updateValidatorsError && (
<p>
{t(
'account.validator.loadingError',
"Couldn't load validators. Showing validator list as of {{staleTimestamp}}.",
{
staleTimestamp: new Date(validatorsTimestamp).toLocaleString(),
},
)}
{t('account.validator.loadingError', "Couldn't load validators.")}{' '}
{validators.length > 0 &&
t('account.validator.showingStale', 'Showing validator list as of {{staleTimestamp}}.', {
staleTimestamp: new Date(validatorsTimestamp!).toLocaleString(),
})}
<br />
{updateValidatorsError}
</p>
Expand Down
25 changes: 7 additions & 18 deletions src/app/state/staking/index.ts
Expand Up @@ -3,21 +3,12 @@ import { createSlice } from 'utils/@reduxjs/toolkit'
import { useInjectReducer, useInjectSaga } from 'utils/redux-injectors'

import { stakingSaga } from './saga'
import { DebondingDelegation, Delegation, StakingState, Validator, ValidatorDetails } from './types'
import * as dump_validators from 'vendors/oasisscan/dump_validators.json'
import { DebondingDelegation, Delegation, StakingState, Validators, ValidatorDetails } from './types'

export const initialState: StakingState = {
debondingDelegations: [],
delegations: [],
validators: {
timestamp: dump_validators.dump_timestamp,
list: dump_validators.list.map(v => {
return {
...v,
status: 'unknown',
}
}),
},
validators: null,
updateValidatorsError: null,
selectedValidatorDetails: null,
selectedValidator: null,
Expand All @@ -32,15 +23,13 @@ const slice = createSlice({
state.loading = action.payload
},
fetchAccount(state, action: PayloadAction<string>) {},
updateValidators(state, action: PayloadAction<Validator[]>) {
updateValidators(state, action: PayloadAction<Validators>) {
state.updateValidatorsError = null
state.validators = {
timestamp: Date.now(),
list: action.payload,
}
state.validators = action.payload
},
updateValidatorsError(state, action: PayloadAction<string>) {
state.updateValidatorsError = action.payload
updateValidatorsError(state, action: PayloadAction<{ error: string; validators: Validators }>) {
state.updateValidatorsError = action.payload.error
state.validators = action.payload.validators
},
updateDelegations(state, action: PayloadAction<Delegation[]>) {
state.delegations = action.payload
Expand Down
78 changes: 77 additions & 1 deletion src/app/state/staking/saga.test.ts
Expand Up @@ -4,11 +4,12 @@ import { expectSaga } from 'redux-saga-test-plan'
import * as matchers from 'redux-saga-test-plan/matchers'
import { EffectProviders, StaticProvider } from 'redux-saga-test-plan/providers'
import { select } from 'redux-saga/effects'
import { RootState } from 'types'

import { initialState, stakingActions, stakingReducer } from '.'
import { getExplorerAPIs, getOasisNic } from '../network/saga'
import { selectEpoch } from '../network/selectors'
import { fetchAccount, stakingSaga } from './saga'
import { fetchAccount, getMainnetDumpValidators, refreshValidators, now, stakingSaga } from './saga'
import { StakingState, Validator } from './types'

const qty = (number: number) => oasis.quantity.fromBigInt(BigInt(number))
Expand All @@ -29,11 +30,13 @@ describe('Staking Sagas', () => {
stakingAccount: jest.fn(),
stakingDebondingDelegationInfosFor: jest.fn(),
stakingDelegationInfosFor: jest.fn(),
schedulerGetValidators: jest.fn(),
}

const providers: (EffectProviders | StaticProvider)[] = [
[matchers.call.fn(getExplorerAPIs), { getAllValidators }],
[matchers.call.fn(getOasisNic), nic],
[matchers.call.fn(now), new Date('2022').getTime()],
]
const validAddress = 'oasis1qqty93azxp4qeft3krvv23ljyj57g3tzk56tqhqe'

Expand Down Expand Up @@ -118,4 +121,77 @@ describe('Staking Sagas', () => {
])
})
})

describe('Fetch validators fallbacks', () => {
it('should load validators when switching network', () => {
getAllValidators.mockResolvedValue([{ address: 'fromApi' }] as Validator[])
return expectSaga(refreshValidators)
.withState({
network: { selectedNetwork: 'testnet' },
staking: { validators: { network: 'mainnet', list: [{ address: 'existing' }] } },
} as RootState)
.provide(providers)
.put(
stakingActions.updateValidators({
timestamp: new Date('2022').getTime(),
network: 'testnet',
list: [{ address: 'fromApi' }] as Validator[],
}),
)
.run()
})

it('should use fallback on mainnet', () => {
getAllValidators.mockRejectedValue('apiFailed')
const getMainnetDumpValidatorsMock = {
dump_timestamp: 1647996761337,
dump_timestamp_iso: '2022-03-23T00:52:41.337Z',
list: [
{
rank: 1,
address: 'oasis1qq3xrq0urs8qcffhvmhfhz4p0mu7ewc8rscnlwxe',
name: 'stakefish',
nodeAddress: 'oasis1qrg52ccz4ts6cct2qu4retxn7kkdlusjh5pe74ar',
status: 'active',
_expectedStatus: 'active' as const,
},
{
rank: 2,
address: 'oasis1qqekv2ymgzmd8j2s2u7g0hhc7e77e654kvwqtjwm',
name: 'BinanceStaking',
nodeAddress: 'oasis1qqp0h2h92eev7nsxgqctvuegt8ge3vyg0qyluc4k',
status: 'active',
_expectedStatus: 'inactive' as const,
},
],
}
nic.schedulerGetValidators.mockResolvedValue([
{
// oasis1qrg52ccz4ts6cct2qu4retxn7kkdlusjh5pe74ar
id: oasis.misc.fromHex('91e7768ae47cd1641d6f883b97e3ea6d0286240bc3e3e2953c5c2e0dce6753a3'),
voting_power: 1,
},
] as oasis.types.SchedulerValidator[])

return expectSaga(refreshValidators)
.withState({
network: { selectedNetwork: 'mainnet' },
} as RootState)
.provide([...providers, [matchers.call.fn(getMainnetDumpValidators), getMainnetDumpValidatorsMock]])
.put(
stakingActions.updateValidatorsError({
error: 'apiFailed',
validators: {
timestamp: getMainnetDumpValidatorsMock.dump_timestamp,
network: 'mainnet',
list: getMainnetDumpValidatorsMock.list.map((v, ix) => ({
...v,
status: v._expectedStatus,
})),
},
}),
)
.run()
})
})
})

0 comments on commit 481a81d

Please sign in to comment.