Don't fail full block decoding on event-only errors (#7878)

This commit is contained in:
Jaco 2022-07-15 16:43:17 +02:00 committed by GitHub
parent 56087a775e
commit 38e3206efe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 41 deletions

View File

@ -26,7 +26,7 @@ interface Props {
}
interface State {
events?: KeyedEvent[];
events?: KeyedEvent[] | null;
getBlock?: SignedBlock;
getHeader?: HeaderExtended;
runtimeVersion?: RuntimeVersionPartial;
@ -34,9 +34,9 @@ interface State {
const EMPTY_HEADER = [['...', 'start', 6]];
function transformResult ([[runtimeVersion, events], getBlock, getHeader]: [[RuntimeVersionPartial, EventRecord[]], SignedBlock, HeaderExtended?]): State {
function transformResult ([[runtimeVersion, events], getBlock, getHeader]: [[RuntimeVersionPartial, EventRecord[] | null], SignedBlock, HeaderExtended?]): State {
return {
events: events.map((record, index) => ({
events: events && events.map((record, index) => ({
indexes: [index],
key: `${Date.now()}-${index}-${record.hash.toHex()}`,
record
@ -52,7 +52,8 @@ function BlockByHash ({ className = '', error, value }: Props): React.ReactEleme
const { api } = useApi();
const mountedRef = useIsMountedRef();
const [{ events, getBlock, getHeader, runtimeVersion }, setState] = useState<State>({});
const [myError, setError] = useState<Error | null | undefined>(error);
const [blkError, setBlkError] = useState<Error | null | undefined>(error);
const [evtError, setEvtError] = useState<Error | null | undefined>();
const isVersionCurrent = useMemo(
() => !!runtimeVersion && api.runtimeVersion.specName.eq(runtimeVersion.specName) && api.runtimeVersion.specVersion.eq(runtimeVersion.specVersion),
@ -62,12 +63,20 @@ function BlockByHash ({ className = '', error, value }: Props): React.ReactEleme
useEffect((): void => {
value && Promise
.all([
api.at(value).then((apiAt) =>
Promise.all([
Promise.resolve(apiAt.runtimeVersion),
apiAt.query.system.events()
])
),
api
.at(value)
.then((apiAt) =>
Promise.all([
Promise.resolve(apiAt.runtimeVersion),
apiAt.query.system
.events()
.catch((error: Error) => {
mountedRef.current && setEvtError(error);
return null;
})
])
),
api.rpc.chain.getBlock(value),
api.derive.chain.getHeader(value)
])
@ -75,7 +84,7 @@ function BlockByHash ({ className = '', error, value }: Props): React.ReactEleme
mountedRef.current && setState(transformResult(result));
})
.catch((error: Error): void => {
mountedRef.current && setError(error);
mountedRef.current && setBlkError(error);
});
}, [api, mountedRef, value]);
@ -108,8 +117,8 @@ function BlockByHash ({ className = '', error, value }: Props): React.ReactEleme
header={header}
isFixed
>
{myError
? <tr><td colSpan={6}>{t('Unable to retrieve the specified block details. {{error}}', { replace: { error: myError.message } })}</td></tr>
{blkError
? <tr><td colSpan={6}>{t('Unable to retrieve the specified block details. {{error}}', { replace: { error: blkError.message } })}</td></tr>
: getBlock && getHeader && !getBlock.isEmpty && !getHeader.isEmpty && (
<tr>
<td className='address'>
@ -146,8 +155,9 @@ function BlockByHash ({ className = '', error, value }: Props): React.ReactEleme
<Columar>
<Columar.Column>
<Events
error={evtError}
eventClassName='explorer--BlockByHash-block'
events={events?.filter(({ record: { phase } }) => !phase.isApplyExtrinsic)}
events={events && events.filter(({ record: { phase } }) => !phase.isApplyExtrinsic)}
label={t<string>('system events')}
/>
</Columar.Column>

View File

@ -17,7 +17,7 @@ import { useTranslation } from '../translate';
interface Props {
blockNumber?: BlockNumber;
className?: string;
events?: KeyedEvent[];
events?: KeyedEvent[] | null;
index: number;
maxBlockWeight?: Weight;
value: Extrinsic;
@ -36,11 +36,13 @@ function getEra ({ era }: Extrinsic, blockNumber?: BlockNumber): [number, number
return null;
}
function filterEvents (index: number, events: KeyedEvent[] = [], maxBlockWeight?: Weight): [DispatchInfo | undefined, number, KeyedEvent[]] {
const filtered = events.filter(({ record: { phase } }) =>
phase.isApplyExtrinsic &&
phase.asApplyExtrinsic.eq(index)
);
function filterEvents (index: number, events?: KeyedEvent[] | null, maxBlockWeight?: Weight): [DispatchInfo | undefined, number, KeyedEvent[]] {
const filtered = events
? events.filter(({ record: { phase } }) =>
phase.isApplyExtrinsic &&
phase.asApplyExtrinsic.eq(index)
)
: [];
const infoRecord = filtered.find(({ record: { event: { method, section } } }) =>
section === 'system' &&
['ExtrinsicFailed', 'ExtrinsicSuccess'].includes(method)

View File

@ -15,7 +15,7 @@ import ExtrinsicDisplay from './Extrinsic';
interface Props {
blockNumber?: BlockNumber;
className?: string;
events?: KeyedEvent[];
events?: KeyedEvent[] | null;
label?: React.ReactNode;
value?: Extrinsic[] | null;
withLink: boolean;

View File

@ -14,12 +14,12 @@ import { BN, formatNumber } from '@polkadot/util';
import { useTranslation } from '../translate';
interface Props {
events?: KeyedEvent[];
events?: KeyedEvent[] | null;
maxBlockWeight?: Weight;
signedBlock?: SignedBlock;
}
function extractEventDetails (events?: KeyedEvent[]): [BN?, BN?, BN?] {
function extractEventDetails (events?: KeyedEvent[] | null): [BN?, BN?, BN?] {
return events
? events.reduce(([deposits, transfers, weight], { record: { event: { data, method, section } } }) => [
section === 'balances' && method === 'Deposit'

View File

@ -15,13 +15,14 @@ import { useTranslation } from './translate';
interface Props {
className?: string;
error?: Error;
emptyLabel?: React.ReactNode;
events?: KeyedEvent[];
events?: KeyedEvent[] | null;
eventClassName?: string;
label?: React.ReactNode;
}
function Events ({ className = '', emptyLabel, eventClassName, events, label }: Props): React.ReactElement<Props> {
function Events ({ className = '', emptyLabel, error, eventClassName, events, label }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const header = useMemo(() => [
@ -34,22 +35,32 @@ function Events ({ className = '', emptyLabel, eventClassName, events, label }:
empty={emptyLabel || t<string>('No events available')}
header={header}
>
{events && events.map(({ blockHash, blockNumber, indexes, key, record }): React.ReactNode => (
<tr
className={eventClassName}
key={key}
>
<td className='overflow'>
<Event value={record} />
{blockNumber && (
<div className='event-link'>
{indexes.length !== 1 && <span>({formatNumber(indexes.length)}x)&nbsp;</span>}
<Link to={`/explorer/query/${blockHash || ''}`}>{formatNumber(blockNumber)}-{indexes[0]}</Link>
</div>
)}
</td>
</tr>
))}
{error
? (
<tr
className={eventClassName}
key='error'
>
<td>{error.message}</td>
</tr>
)
: events && events.map(({ blockHash, blockNumber, indexes, key, record }): React.ReactNode => (
<tr
className={eventClassName}
key={key}
>
<td className='overflow'>
<Event value={record} />
{blockNumber && (
<div className='event-link'>
{indexes.length !== 1 && <span>({formatNumber(indexes.length)}x)&nbsp;</span>}
<Link to={`/explorer/query/${blockHash || ''}`}>{formatNumber(blockNumber)}-{indexes[0]}</Link>
</div>
)}
</td>
</tr>
))
}
</Table>
);
}