Make usePreimage deal with inline calls (#8843)
* Make usePreimage deal with inline calls * Ongoing adjustment for hash extraction
This commit is contained in:
parent
ef44a0d206
commit
d12280cb1b
@ -1,6 +1,7 @@
|
||||
// Copyright 2017-2023 @polkadot/app-referenda authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { Hash } from '@polkadot/types/interfaces';
|
||||
import type { PalletConvictionVotingTally, PalletRankedCollectiveTally, PalletReferendaDeposit, PalletReferendaTrackInfo } from '@polkadot/types/lookup';
|
||||
import type { BN } from '@polkadot/util';
|
||||
@ -10,7 +11,8 @@ import type { Referendum, ReferendumProps as Props } from '../types';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { CallExpander, Progress } from '@polkadot/react-components';
|
||||
import { getPreimageHash, usePreimage } from '@polkadot/react-hooks/usePreimage';
|
||||
import { useApi, usePreimage } from '@polkadot/react-hooks';
|
||||
import { getPreimageHash } from '@polkadot/react-hooks/usePreimage';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Deposits from './Deposits';
|
||||
@ -27,15 +29,15 @@ interface Expanded {
|
||||
decideEnd: BN | null;
|
||||
confirmEnd: BN | null;
|
||||
};
|
||||
proposalHash: HexString;
|
||||
proposalHash?: HexString;
|
||||
submissionDeposit: PalletReferendaDeposit | null;
|
||||
tally: PalletConvictionVotingTally | PalletRankedCollectiveTally;
|
||||
tallyTotal: BN;
|
||||
}
|
||||
|
||||
function expandOngoing (info: Referendum['info'], track?: PalletReferendaTrackInfo): Expanded {
|
||||
function expandOngoing (api: ApiPromise, info: Referendum['info'], track?: PalletReferendaTrackInfo): Expanded {
|
||||
const ongoing = info.asOngoing;
|
||||
const proposalHash = getPreimageHash(ongoing.proposal || (ongoing as unknown as { proposalHash: Hash }).proposalHash);
|
||||
const proposalHash = getPreimageHash(api, ongoing.proposal || (ongoing as unknown as { proposalHash: Hash }).proposalHash).proposalHash;
|
||||
let prepareEnd: BN | null = null;
|
||||
let decideEnd: BN | null = null;
|
||||
let confirmEnd: BN | null = null;
|
||||
@ -76,10 +78,11 @@ function expandOngoing (info: Referendum['info'], track?: PalletReferendaTrackIn
|
||||
|
||||
function Ongoing ({ isMember, members, palletReferenda, palletVote, ranks, trackInfo, value: { id, info, isConvictionVote, track } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
|
||||
const { decisionDeposit, periods: { confirmEnd, decideEnd, periodEnd }, proposalHash, submissionDeposit, tally, tallyTotal } = useMemo(
|
||||
() => expandOngoing(info, track),
|
||||
[info, track]
|
||||
() => expandOngoing(api, info, track),
|
||||
[api, info, track]
|
||||
);
|
||||
|
||||
const preimage = usePreimage(proposalHash);
|
||||
|
||||
@ -5,7 +5,6 @@ import type { ApiPromise } from '@polkadot/api';
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/types';
|
||||
import type { DeriveAccountFlags, DeriveAccountRegistration } from '@polkadot/api-derive/types';
|
||||
import type { DisplayedJudgement } from '@polkadot/react-components/types';
|
||||
import type { Bytes } from '@polkadot/types';
|
||||
import type { AccountId, Balance, BlockNumber, Call, Exposure, Hash, RewardDestination, SessionIndex, StakingLedger, ValidatorPrefs } from '@polkadot/types/interfaces';
|
||||
import type { PalletPreimageRequestStatus } from '@polkadot/types/lookup';
|
||||
import type { IExtrinsic, Registry } from '@polkadot/types/types';
|
||||
@ -181,6 +180,7 @@ export interface PreimageStatus {
|
||||
count: number;
|
||||
deposit?: PreimageDeposit;
|
||||
isCompleted: boolean;
|
||||
isHashParam: boolean;
|
||||
proposalHash: HexString;
|
||||
proposalLength?: BN;
|
||||
registry: Registry;
|
||||
@ -188,7 +188,6 @@ export interface PreimageStatus {
|
||||
}
|
||||
|
||||
export interface PreimageBytes {
|
||||
bytes?: Bytes | null;
|
||||
proposal?: Call | null;
|
||||
proposalError?: string | null;
|
||||
proposalWarning?: string | null;
|
||||
|
||||
@ -13,17 +13,28 @@ import { useMemo } from 'react';
|
||||
|
||||
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
import { BN, BN_ZERO, formatNumber, isString, objectSpread } from '@polkadot/util';
|
||||
import { BN, BN_ZERO, formatNumber, isString, isU8a, objectSpread, u8aToHex } from '@polkadot/util';
|
||||
|
||||
type BytesParams = [[proposalHash: HexString, proposalLength: BN]] | [proposalHash: HexString];
|
||||
type BytesParamsType = [[proposalHash: HexString, proposalLength: BN]] | [proposalHash: HexString];
|
||||
|
||||
interface InterimResult {
|
||||
paramsBytes?: BytesParams;
|
||||
preimageStatus?: PreimageStatus;
|
||||
interface BytesParams {
|
||||
paramsBytes?: BytesParamsType;
|
||||
resultPreimageFor?: PreimageStatus;
|
||||
}
|
||||
|
||||
interface StatusParams {
|
||||
inlineData?: Uint8Array;
|
||||
paramsStatus?: [HexString];
|
||||
proposalHash?: HexString;
|
||||
resultPreimageHash?: PreimageStatus;
|
||||
}
|
||||
|
||||
type Result = 'unknown' | 'hash' | 'hashAndLen';
|
||||
|
||||
/**
|
||||
* @internal Determine if we are working with current generation (H256,u32)
|
||||
* or previous generation H256 params to the preimageFor storage entry
|
||||
*/
|
||||
export function getParamType (api: ApiPromise): Result {
|
||||
if ((
|
||||
api.query.preimage &&
|
||||
@ -44,21 +55,64 @@ export function getParamType (api: ApiPromise): Result {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function createResult (api: ApiPromise, preimageStatus: PreimageStatus, optBytes: Option<Bytes>): Preimage {
|
||||
const bytes = optBytes.unwrapOr(null);
|
||||
/** @internal Unwraps a passed preimage hash into components */
|
||||
export function getPreimageHash (api: ApiPromise, hashOrBounded: Hash | HexString | FrameSupportPreimagesBounded): StatusParams {
|
||||
let proposalHash: HexString | undefined;
|
||||
let inlineData: Uint8Array | undefined;
|
||||
|
||||
if (isString(hashOrBounded)) {
|
||||
proposalHash = hashOrBounded;
|
||||
} else if (isU8a(hashOrBounded)) {
|
||||
proposalHash = hashOrBounded.toHex();
|
||||
} else {
|
||||
const bounded = hashOrBounded;
|
||||
|
||||
if (bounded.isInline) {
|
||||
inlineData = bounded.asInline.toU8a(true);
|
||||
proposalHash = u8aToHex(api.registry.hash(inlineData));
|
||||
} else if (hashOrBounded.isLegacy) {
|
||||
proposalHash = hashOrBounded.asLegacy.hash_.toHex();
|
||||
} else if (hashOrBounded.isLookup) {
|
||||
proposalHash = hashOrBounded.asLookup.hash_.toHex();
|
||||
} else {
|
||||
console.error(`Unhandled FrameSupportPreimagesBounded type ${hashOrBounded.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
inlineData,
|
||||
paramsStatus: proposalHash && [proposalHash],
|
||||
proposalHash,
|
||||
resultPreimageHash: proposalHash && {
|
||||
count: 0,
|
||||
isCompleted: false,
|
||||
isHashParam: getParamType(api) === 'hash',
|
||||
proposalHash,
|
||||
proposalLength: inlineData && new BN(inlineData.length),
|
||||
registry: api.registry,
|
||||
status: null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @internal Creates a final result */
|
||||
function createResult (interimResult: PreimageStatus, optBytes: Option<Bytes> | Uint8Array): Preimage {
|
||||
const callData = isU8a(optBytes)
|
||||
? optBytes
|
||||
: optBytes.unwrapOr(null);
|
||||
let proposal: Call | null = null;
|
||||
let proposalError: string | null = null;
|
||||
let proposalWarning: string | null = null;
|
||||
let proposalLength: BN | undefined;
|
||||
|
||||
if (bytes) {
|
||||
if (callData) {
|
||||
try {
|
||||
proposal = api.registry.createType('Call', bytes.toU8a(true));
|
||||
proposal = interimResult.registry.createType('Call', callData);
|
||||
|
||||
const callLength = proposal.encodedLength;
|
||||
|
||||
if (preimageStatus.proposalLength) {
|
||||
const storeLength = preimageStatus.proposalLength.toNumber();
|
||||
if (interimResult.proposalLength) {
|
||||
const storeLength = interimResult.proposalLength.toNumber();
|
||||
|
||||
if (callLength !== storeLength) {
|
||||
proposalWarning = `Decoded call length does not match on-chain stored preimage length (${formatNumber(callLength)} bytes vs ${formatNumber(storeLength)} bytes)`;
|
||||
@ -76,17 +130,16 @@ function createResult (api: ApiPromise, preimageStatus: PreimageStatus, optBytes
|
||||
proposalWarning = 'No preimage bytes found';
|
||||
}
|
||||
|
||||
return objectSpread<Preimage>({}, preimageStatus, {
|
||||
bytes,
|
||||
return objectSpread<Preimage>({}, interimResult, {
|
||||
isCompleted: true,
|
||||
proposal,
|
||||
proposalError,
|
||||
proposalLength: proposalLength || preimageStatus.proposalLength,
|
||||
proposalWarning,
|
||||
registry: api.registry
|
||||
proposalLength: proposalLength || interimResult.proposalLength,
|
||||
proposalWarning
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal Helper to unwrap a deposit tuple into a structure */
|
||||
function convertDeposit (deposit?: [AccountId, Balance] | null): PreimageDeposit | undefined {
|
||||
return deposit
|
||||
? {
|
||||
@ -96,22 +149,15 @@ function convertDeposit (deposit?: [AccountId, Balance] | null): PreimageDeposit
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function getBytesParams (api: ApiPromise, hash: Hash | HexString, optStatus: Option<PalletPreimageRequestStatus>): InterimResult {
|
||||
const isHashParam = getParamType(api) === 'hash';
|
||||
const status = optStatus.unwrapOr(null);
|
||||
const preimageStatus: PreimageStatus = {
|
||||
count: 0,
|
||||
isCompleted: false,
|
||||
proposalHash: isString(hash)
|
||||
? hash
|
||||
: hash.toHex(),
|
||||
registry: api.registry,
|
||||
status
|
||||
};
|
||||
/** @internal Returns the parameters required for a call to bytes */
|
||||
function getBytesParams (interimResult: PreimageStatus, optStatus: Option<PalletPreimageRequestStatus>): BytesParams {
|
||||
const result = objectSpread<PreimageStatus>({}, interimResult, {
|
||||
status: optStatus.unwrapOr(null)
|
||||
});
|
||||
|
||||
if (status) {
|
||||
if (status.isRequested) {
|
||||
const asRequested = status.asRequested;
|
||||
if (result.status) {
|
||||
if (result.status.isRequested) {
|
||||
const asRequested = result.status.asRequested;
|
||||
|
||||
if (asRequested instanceof Option) {
|
||||
// FIXME Cannot recall how to deal with these
|
||||
@ -119,87 +165,72 @@ function getBytesParams (api: ApiPromise, hash: Hash | HexString, optStatus: Opt
|
||||
} else {
|
||||
const { count, deposit, len } = asRequested;
|
||||
|
||||
preimageStatus.count = count.toNumber();
|
||||
preimageStatus.deposit = convertDeposit(deposit.unwrapOr(null));
|
||||
preimageStatus.proposalLength = len.unwrapOr(BN_ZERO);
|
||||
result.count = count.toNumber();
|
||||
result.deposit = convertDeposit(deposit.unwrapOr(null));
|
||||
result.proposalLength = len.unwrapOr(BN_ZERO);
|
||||
}
|
||||
} else if (status.isUnrequested) {
|
||||
const asUnrequested = status.asUnrequested;
|
||||
} else if (result.status.isUnrequested) {
|
||||
const asUnrequested = result.status.asUnrequested;
|
||||
|
||||
if (asUnrequested instanceof Option) {
|
||||
preimageStatus.deposit = convertDeposit(
|
||||
result.deposit = convertDeposit(
|
||||
// old-style conversion
|
||||
(asUnrequested as Option<ITuple<[AccountId, Balance]>>).unwrapOr(null)
|
||||
);
|
||||
} else {
|
||||
const { deposit, len } = status.asUnrequested;
|
||||
const { deposit, len } = result.status.asUnrequested;
|
||||
|
||||
preimageStatus.deposit = convertDeposit(deposit);
|
||||
preimageStatus.proposalLength = len;
|
||||
result.deposit = convertDeposit(deposit);
|
||||
result.proposalLength = len;
|
||||
}
|
||||
} else {
|
||||
// for future reference...
|
||||
console.error('Unhandled preimage status type: ', status.type);
|
||||
console.error(`Unhandled PalletPreimageRequestStatus type: ${result.status.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
paramsBytes: isHashParam
|
||||
? [preimageStatus.proposalHash]
|
||||
: [[preimageStatus.proposalHash, preimageStatus.proposalLength || BN_ZERO]],
|
||||
preimageStatus
|
||||
paramsBytes: result.isHashParam
|
||||
? [result.proposalHash]
|
||||
: [[result.proposalHash, result.proposalLength || BN_ZERO]],
|
||||
resultPreimageFor: result
|
||||
};
|
||||
}
|
||||
|
||||
export function getPreimageHash (hashOrBounded: Hash | HexString | FrameSupportPreimagesBounded): HexString {
|
||||
if (isString(hashOrBounded)) {
|
||||
return hashOrBounded;
|
||||
}
|
||||
|
||||
const bounded = hashOrBounded as FrameSupportPreimagesBounded;
|
||||
|
||||
return bounded.isInline
|
||||
? bounded.asInline.hash.toHex()
|
||||
: bounded.isLegacy
|
||||
? bounded.asLegacy.hash_.toHex()
|
||||
: bounded.isLookup
|
||||
? bounded.asLookup.hash_.toHex()
|
||||
: hashOrBounded.toHex();
|
||||
}
|
||||
|
||||
function usePreimageImpl (hashOrBounded?: Hash | HexString | FrameSupportPreimagesBounded | null): Preimage | undefined {
|
||||
const { api } = useApi();
|
||||
|
||||
// retrieve the status using only the hash of the image
|
||||
const paramsStatus = useMemo(
|
||||
// we need a hash _and_ be on the newest supported version of the pallet
|
||||
// (after the application of bounded calls)
|
||||
const { inlineData, paramsStatus, resultPreimageHash } = useMemo(
|
||||
() => hashOrBounded
|
||||
? [getPreimageHash(hashOrBounded)]
|
||||
: undefined,
|
||||
[hashOrBounded]
|
||||
? getPreimageHash(api, hashOrBounded)
|
||||
: {},
|
||||
[api, hashOrBounded]
|
||||
);
|
||||
|
||||
const optStatus = useCall<Option<PalletPreimageRequestStatus>>(paramsStatus && api.query.preimage?.statusFor, paramsStatus);
|
||||
|
||||
// from the retrieved status (if any), get the on-chain stored bytes
|
||||
const { paramsBytes, preimageStatus } = useMemo(
|
||||
() => paramsStatus && optStatus
|
||||
? getBytesParams(api, paramsStatus[0], optStatus)
|
||||
const { paramsBytes, resultPreimageFor } = useMemo(
|
||||
() => resultPreimageHash && optStatus
|
||||
? getBytesParams(resultPreimageHash, optStatus)
|
||||
: {},
|
||||
[api, optStatus, paramsStatus]
|
||||
[optStatus, resultPreimageHash]
|
||||
);
|
||||
|
||||
const optBytes = useCall<Option<Bytes>>(paramsBytes && api.query.preimage?.preimageFor, paramsBytes);
|
||||
|
||||
// extract all the preimage info we have retrieved
|
||||
return useMemo(
|
||||
() => preimageStatus
|
||||
() => resultPreimageFor
|
||||
? optBytes
|
||||
? createResult(api, preimageStatus, optBytes)
|
||||
: preimageStatus
|
||||
: undefined,
|
||||
[api, optBytes, preimageStatus]
|
||||
? createResult(resultPreimageFor, optBytes)
|
||||
: resultPreimageFor
|
||||
: resultPreimageHash
|
||||
? inlineData
|
||||
? createResult(resultPreimageHash, inlineData)
|
||||
: resultPreimageHash
|
||||
: undefined,
|
||||
[inlineData, optBytes, resultPreimageHash, resultPreimageFor]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user