diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ab538a4d8..21edc82a54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Changes: +- Re-add isPedantic check on storage decoding - Additional Xcm types diff --git a/packages/api/src/base/capabilities.ts b/packages/api/src/base/capabilities.ts index 44578ecae1..d9174f0867 100644 --- a/packages/api/src/base/capabilities.ts +++ b/packages/api/src/base/capabilities.ts @@ -11,7 +11,7 @@ import { combineLatest, Observable, of } from '@polkadot/x-rxjs'; import { map, take } from '@polkadot/x-rxjs/operators'; // the order and types needs to map with the all array setup below -type ExtractedQ = [bool | null, bool | null, bool | null, Releases | null]; +type ExtractedQ = [u32 | null, bool | null, bool | null, bool | null, Releases | null]; type ExtractedR = [Raw | null]; @@ -42,7 +42,7 @@ interface Constants { const NumberMap = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten']; -function mapCapabilities ({ accountIdLength }: Constants, [leasePeriodsPerSlot, slotRangeCount]: ExtractedC, [systemRefcount32, systemRefcountDual, systemRefcountTriple, stakingVersion]: ExtractedQ, [keys]: ExtractedR): Partial { +function mapCapabilities ({ accountIdLength }: Constants, [leasePeriodsPerSlot, slotRangeCount]: ExtractedC, [systemBlockNumber, systemRefcount32, systemRefcountDual, systemRefcountTriple, stakingVersion]: ExtractedQ, [keys]: ExtractedR): Partial { const types: Partial = {}; // AccountInfo @@ -50,7 +50,7 @@ function mapCapabilities ({ accountIdLength }: Constants, [leasePeriodsPerSlot, types.AccountInfo = 'AccountInfoWithTripleRefCount'; } else if (systemRefcountDual && systemRefcountDual.isTrue) { types.AccountInfo = 'AccountInfoWithDualRefCount'; - } else { + } else if (systemBlockNumber && !systemBlockNumber.isEmpty) { types.AccountInfo = 'AccountInfoWithRefCount'; if (!systemRefcount32 || systemRefcount32.isFalse) { @@ -139,6 +139,7 @@ export function detectedCapabilities (api: ApiInterfaceRx, blockHash?: Uint8Arra api.consts.auctions?.slotRangeCount ]); const queries = filterEntries([ + api.query.system?.number, api.query.system?.upgradedToU32RefCount, api.query.system?.upgradedToDualRefCount, api.query.system?.upgradedToTripleRefCount, diff --git a/packages/types/src/create/createType.ts b/packages/types/src/create/createType.ts index 740f222372..f1fd275bb3 100644 --- a/packages/types/src/create/createType.ts +++ b/packages/types/src/create/createType.ts @@ -4,7 +4,7 @@ import type { Codec, Constructor, InterfaceTypes, Registry } from '../types'; import type { FromReg } from './types'; -import { logger } from '@polkadot/util'; +import { assert, isHex, isU8a, logger, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util'; import { createClass } from './createClass'; @@ -16,47 +16,37 @@ interface CreateOptions { const l = logger('registry'); -// TODO: Fix and re-enable // With isPedantic, actually check that the encoding matches that supplied. This // is much slower, but verifies that we have the correct types defined -// function checkInstance (value: Uint8Array, created: FromReg): void { -// // the underlying type created.toRawType() -// const rawType = created.toRawType(); +// eslint-disable-next-line @typescript-eslint/ban-types +function checkInstance (value: Uint8Array, created: FromReg): void { + const u8a = created.toU8a(); + const rawType = created.toRawType(); + const isEqual = u8aEq(value, u8a) || + // :code entries and singular bytes don't have length-prefixes (when from storage) + (rawType === 'Bytes' && value.length === u8a.length); -// // ignore bytes completely - this is probably a FIXME, since these are somewhat -// // breaking for at least online queries - not quite sure wtf is going wrong here -// if (rawType === 'Bytes') { -// return; -// } - -// // the hex values for what we have -// const inHex = u8aToHex(value); -// const crHex = created.toHex(); - -// // Check equality, based on some different approaches (as decoded) -// const isEqual = inHex === crHex || // raw hex values, quick path -// inHex === created.toHex(true) || // wrapped options -// u8aToHex(value.reverse()) === crHex; // reverse (for numbers, which are BE) - -// // if the hex doesn't match and the value for both is non-empty, complain... bitterly -// if (!isEqual && (u8aHasValue(value) || u8aHasValue(created.toU8a(true)))) { -// l.warn(`${rawType}:: Input doesn't match output, received ${u8aToHex(value)}, created ${crHex}`); -// } -// } + assert(isEqual, () => `${rawType}:: Decoded input doesn't match input, received ${u8aToHex(value, 512)} (${value.length} bytes), created ${u8aToHex(u8a, 512)} (${u8a.length} bytes)`); +} // Initializes a type with a value. This also checks for fallbacks and in the cases // where isPedantic is specified (storage decoding), also check the format/structure -function initType (registry: Registry, Type: Constructor>, params: any[] = [], { blockHash }: CreateOptions = {}): FromReg { +function initType (registry: Registry, Type: Constructor>, params: unknown[] = [], { blockHash, isPedantic }: CreateOptions = {}): FromReg { const created = new Type(registry, ...params); + const value = params[0]; + + if (isPedantic) { + if (isU8a(value)) { + checkInstance(value, created); + } else if (isHex(value)) { + checkInstance(u8aToU8a(value.toString()), created); + } + } if (blockHash) { created.createdAtHash = createType(registry, 'Hash', blockHash); } - // if (isPedantic && isU8a(value)) { - // checkInstance(value, created); - // } - return created; }