diff --git a/jest.config.js b/jest.config.js index 17ce455e6..64f0f026e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -39,6 +39,7 @@ module.exports = { '!did/src/Did.rpc.ts', '!did/src/Did.utils.ts', '!utils/src/jsonabc.ts', + '!core/src/utils.ts', ], resolver: "ts-jest-resolver", rootDir: 'packages', diff --git a/packages/augment-api/src/interfaces/augment-api-events.ts b/packages/augment-api/src/interfaces/augment-api-events.ts index a8c3e78e8..a593a09a9 100644 --- a/packages/augment-api/src/interfaces/augment-api-events.ts +++ b/packages/augment-api/src/interfaces/augment-api-events.ts @@ -119,6 +119,11 @@ declare module '@polkadot/api-base/types/events' { * \[creator identifier, CType hash\] **/ CTypeCreated: AugmentedEvent; + /** + * Information about a CType has been updated. + * \[CType hash\] + **/ + CTypeUpdated: AugmentedEvent; }; cumulusXcm: { /** diff --git a/packages/augment-api/src/interfaces/augment-api-query.ts b/packages/augment-api/src/interfaces/augment-api-query.ts index 28c428346..ca8807ebe 100644 --- a/packages/augment-api/src/interfaces/augment-api-query.ts +++ b/packages/augment-api/src/interfaces/augment-api-query.ts @@ -9,7 +9,7 @@ import type { ApiTypes, AugmentedQuery, QueryableStorageEntry } from '@polkadot/ import type { BTreeMap, Bytes, Null, Option, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types-codec'; import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, Call, H256, Weight } from '@polkadot/types/interfaces/runtime'; -import type { AttestationAttestationsAttestationDetails, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, DelegationDelegationHierarchyDelegationHierarchyDetails, DelegationDelegationHierarchyDelegationNode, DidDidDetails, DidServiceEndpointsDidEndpoint, FrameSupportWeightsPerDispatchClassWeight, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, KiltAssetDidsV1AssetDid, PalletAuthorshipUncleEntryItem, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletCollectiveVotes, PalletDemocracyPreimageStatus, PalletDemocracyReferendumInfo, PalletDemocracyReleases, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletDidLookupConnectionRecord, PalletDidLookupLinkableAccountLinkableAccountId, PalletPreimageRequestStatus, PalletProxyAnnouncement, PalletProxyProxyDefinition, PalletSchedulerScheduledV3, PalletTipsOpenTip, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletVestingReleases, PalletVestingVestingInfo, PalletWeb3NamesWeb3NameWeb3NameOwnership, ParachainStakingCandidate, ParachainStakingDelegationCounter, ParachainStakingInflationInflationInfo, ParachainStakingRoundInfo, ParachainStakingStake, ParachainStakingTotalStake, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, PublicCredentialsCredentialsCredentialEntry, RuntimeCommonAssetsAssetDid, RuntimeCommonAuthorizationAuthorizationId, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpRuntimeDigest, SpTrieStorageProof, SpiritnetRuntimeSessionKeys } from '@polkadot/types/lookup'; +import type { AttestationAttestationsAttestationDetails, CtypeCtypeEntry, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, DelegationDelegationHierarchyDelegationHierarchyDetails, DelegationDelegationHierarchyDelegationNode, DidDidDetails, DidServiceEndpointsDidEndpoint, FrameSupportWeightsPerDispatchClassWeight, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, KiltAssetDidsV1AssetDid, PalletAuthorshipUncleEntryItem, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletCollectiveVotes, PalletDemocracyPreimageStatus, PalletDemocracyReferendumInfo, PalletDemocracyReleases, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletDidLookupConnectionRecord, PalletDidLookupLinkableAccountLinkableAccountId, PalletPreimageRequestStatus, PalletProxyAnnouncement, PalletProxyProxyDefinition, PalletSchedulerScheduledV3, PalletTipsOpenTip, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletVestingReleases, PalletVestingVestingInfo, PalletWeb3NamesWeb3NameWeb3NameOwnership, ParachainStakingCandidate, ParachainStakingDelegationCounter, ParachainStakingInflationInflationInfo, ParachainStakingRoundInfo, ParachainStakingStake, ParachainStakingTotalStake, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, PublicCredentialsCredentialsCredentialEntry, RuntimeCommonAssetsAssetDid, RuntimeCommonAuthorizationAuthorizationId, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpRuntimeDigest, SpTrieStorageProof, SpiritnetRuntimeSessionKeys } from '@polkadot/types/lookup'; import type { Observable } from '@polkadot/types/types'; export type __AugmentedQuery = AugmentedQuery unknown>; @@ -145,9 +145,10 @@ declare module '@polkadot/api-base/types/storage' { /** * CTypes stored on chain. * - * It maps from a CType hash to its creator. + * It maps from a CType hash to its creator and block number in which it + * was created. **/ - ctypes: AugmentedQuery Observable>, [H256]>; + ctypes: AugmentedQuery Observable>, [H256]>; }; delegation: { /** diff --git a/packages/augment-api/src/interfaces/augment-api-tx.ts b/packages/augment-api/src/interfaces/augment-api-tx.ts index 8094a07e1..85f622c21 100644 --- a/packages/augment-api/src/interfaces/augment-api-tx.ts +++ b/packages/augment-api/src/interfaces/augment-api-tx.ts @@ -369,6 +369,12 @@ declare module '@polkadot/api-base/types/submittable' { * # **/ add: AugmentedSubmittable<(ctype: Bytes | string | Uint8Array) => SubmittableExtrinsic, [Bytes]>; + /** + * Set the creation block number for a given CType, if found. + * + * Emits `CTypeUpdated`. + **/ + setBlockNumber: AugmentedSubmittable<(ctypeHash: H256 | string | Uint8Array, blockNumber: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [H256, u64]>; }; delegation: { /** diff --git a/packages/augment-api/src/interfaces/lookup.ts b/packages/augment-api/src/interfaces/lookup.ts index 9b0cb8e80..9558566c5 100644 --- a/packages/augment-api/src/interfaces/lookup.ts +++ b/packages/augment-api/src/interfaces/lookup.ts @@ -623,7 +623,8 @@ export default { **/ CtypeEvent: { _enum: { - CTypeCreated: '(AccountId32,H256)' + CTypeCreated: '(AccountId32,H256)', + CTypeUpdated: 'H256' } }, /** @@ -2482,7 +2483,11 @@ export default { CtypeCall: { _enum: { add: { - ctype: 'Bytes' + ctype: 'Bytes', + }, + set_block_number: { + ctypeHash: 'H256', + blockNumber: 'u64' } } }, @@ -3332,6 +3337,13 @@ export default { tips: 'Vec<(AccountId32,u128)>', findersFee: 'bool' }, + /** + * Lookup396: ctype::ctype_entry::CtypeEntry + **/ + CtypeCtypeEntry: { + creator: 'AccountId32', + createdAt: 'u64' + }, /** * Lookup398: pallet_tips::pallet::Error **/ diff --git a/packages/augment-api/src/interfaces/registry.ts b/packages/augment-api/src/interfaces/registry.ts index 5647bbcc9..48b972b4f 100644 --- a/packages/augment-api/src/interfaces/registry.ts +++ b/packages/augment-api/src/interfaces/registry.ts @@ -5,7 +5,7 @@ // this is required to allow for ambient/previous definitions import '@polkadot/types/types/registry'; -import type { AttestationAttestationsAttestationDetails, AttestationCall, AttestationError, AttestationEvent, CtypeCall, CtypeError, CtypeEvent, CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, DelegationAccessControlDelegationAc, DelegationCall, DelegationDelegationHierarchyDelegationDetails, DelegationDelegationHierarchyDelegationHierarchyDetails, DelegationDelegationHierarchyDelegationNode, DelegationDelegationHierarchyPermissions, DelegationError, DelegationEvent, DidCall, DidDidDetails, DidDidDetailsDidAuthorizedCallOperation, DidDidDetailsDidCreationDetails, DidDidDetailsDidEncryptionKey, DidDidDetailsDidPublicKey, DidDidDetailsDidPublicKeyDetails, DidDidDetailsDidSignature, DidDidDetailsDidVerificationKey, DidError, DidEvent, DidOriginDidRawOrigin, DidServiceEndpointsDidEndpoint, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassWeight, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonZeroSender, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, KiltAssetDidsAssetV1AssetId, KiltAssetDidsAssetV1EvmSmartContractFungibleReference, KiltAssetDidsAssetV1EvmSmartContractNonFungibleReference, KiltAssetDidsAssetV1GenericAssetId, KiltAssetDidsChainV1ChainId, KiltAssetDidsChainV1GenericChainId, KiltAssetDidsChainV1GenesisHexHash32Reference, KiltAssetDidsV1AssetDid, KiltSupportDeposit, PalletAuthorshipCall, PalletAuthorshipError, PalletAuthorshipUncleEntryItem, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyPreimageStatus, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyReleases, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletDidLookupAccountAccountId20, PalletDidLookupAccountEthereumSignature, PalletDidLookupAssociateAccountRequest, PalletDidLookupCall, PalletDidLookupConnectionRecord, PalletDidLookupError, PalletDidLookupEvent, PalletDidLookupLinkableAccountLinkableAccountId, PalletIndicesCall, PalletIndicesError, PalletIndicesEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletProxyAnnouncement, PalletProxyCall, PalletProxyError, PalletProxyEvent, PalletProxyProxyDefinition, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduledV3, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletTimestampCall, PalletTipsCall, PalletTipsError, PalletTipsEvent, PalletTipsOpenTip, PalletTransactionPaymentChargeTransactionPayment, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletVestingCall, PalletVestingError, PalletVestingEvent, PalletVestingReleases, PalletVestingVestingInfo, PalletWeb3NamesCall, PalletWeb3NamesError, PalletWeb3NamesEvent, PalletWeb3NamesWeb3NameWeb3NameOwnership, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, ParachainStakingCall, ParachainStakingCandidate, ParachainStakingCandidateStatus, ParachainStakingDelegationCounter, ParachainStakingError, ParachainStakingEvent, ParachainStakingInflationInflationInfo, ParachainStakingInflationRewardRate, ParachainStakingInflationStakingInfo, ParachainStakingRoundInfo, ParachainStakingSetOrderedSet, ParachainStakingStake, ParachainStakingTotalStake, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, PublicCredentialsCall, PublicCredentialsCredentialsCredential, PublicCredentialsCredentialsCredentialEntry, PublicCredentialsError, PublicCredentialsEvent, RuntimeCommonAssetsAssetDid, RuntimeCommonAuthorizationAuthorizationId, RuntimeCommonAuthorizationPalletAuthorize, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaPublic, SpCoreEcdsaSignature, SpCoreEd25519Public, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeBlakeTwo256, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeHeader, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpiritnetRuntimeOriginCaller, SpiritnetRuntimeProxyType, SpiritnetRuntimeRuntime, SpiritnetRuntimeSessionKeys, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; +import type { AttestationAttestationsAttestationDetails, AttestationCall, AttestationError, AttestationEvent, CtypeCall, CtypeError, CtypeCtypeEntry, CtypeEvent, CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, DelegationAccessControlDelegationAc, DelegationCall, DelegationDelegationHierarchyDelegationDetails, DelegationDelegationHierarchyDelegationHierarchyDetails, DelegationDelegationHierarchyDelegationNode, DelegationDelegationHierarchyPermissions, DelegationError, DelegationEvent, DidCall, DidDidDetails, DidDidDetailsDidAuthorizedCallOperation, DidDidDetailsDidCreationDetails, DidDidDetailsDidEncryptionKey, DidDidDetailsDidPublicKey, DidDidDetailsDidPublicKeyDetails, DidDidDetailsDidSignature, DidDidDetailsDidVerificationKey, DidError, DidEvent, DidOriginDidRawOrigin, DidServiceEndpointsDidEndpoint, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassWeight, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonZeroSender, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, KiltAssetDidsAssetV1AssetId, KiltAssetDidsAssetV1EvmSmartContractFungibleReference, KiltAssetDidsAssetV1EvmSmartContractNonFungibleReference, KiltAssetDidsAssetV1GenericAssetId, KiltAssetDidsChainV1ChainId, KiltAssetDidsChainV1GenericChainId, KiltAssetDidsChainV1GenesisHexHash32Reference, KiltAssetDidsV1AssetDid, KiltSupportDeposit, PalletAuthorshipCall, PalletAuthorshipError, PalletAuthorshipUncleEntryItem, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyPreimageStatus, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyReleases, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletDidLookupAccountAccountId20, PalletDidLookupAccountEthereumSignature, PalletDidLookupAssociateAccountRequest, PalletDidLookupCall, PalletDidLookupConnectionRecord, PalletDidLookupError, PalletDidLookupEvent, PalletDidLookupLinkableAccountLinkableAccountId, PalletIndicesCall, PalletIndicesError, PalletIndicesEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletProxyAnnouncement, PalletProxyCall, PalletProxyError, PalletProxyEvent, PalletProxyProxyDefinition, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduledV3, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletTimestampCall, PalletTipsCall, PalletTipsError, PalletTipsEvent, PalletTipsOpenTip, PalletTransactionPaymentChargeTransactionPayment, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletVestingCall, PalletVestingError, PalletVestingEvent, PalletVestingReleases, PalletVestingVestingInfo, PalletWeb3NamesCall, PalletWeb3NamesError, PalletWeb3NamesEvent, PalletWeb3NamesWeb3NameWeb3NameOwnership, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, ParachainStakingCall, ParachainStakingCandidate, ParachainStakingCandidateStatus, ParachainStakingDelegationCounter, ParachainStakingError, ParachainStakingEvent, ParachainStakingInflationInflationInfo, ParachainStakingInflationRewardRate, ParachainStakingInflationStakingInfo, ParachainStakingRoundInfo, ParachainStakingSetOrderedSet, ParachainStakingStake, ParachainStakingTotalStake, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, PublicCredentialsCall, PublicCredentialsCredentialsCredential, PublicCredentialsCredentialsCredentialEntry, PublicCredentialsError, PublicCredentialsEvent, RuntimeCommonAssetsAssetDid, RuntimeCommonAuthorizationAuthorizationId, RuntimeCommonAuthorizationPalletAuthorize, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaPublic, SpCoreEcdsaSignature, SpCoreEd25519Public, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeBlakeTwo256, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeHeader, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpiritnetRuntimeOriginCaller, SpiritnetRuntimeProxyType, SpiritnetRuntimeRuntime, SpiritnetRuntimeSessionKeys, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; declare module '@polkadot/types/types/registry' { interface InterfaceTypes { @@ -14,6 +14,7 @@ declare module '@polkadot/types/types/registry' { AttestationError: AttestationError; AttestationEvent: AttestationEvent; CtypeCall: CtypeCall; + CtypeCtypeEntry: CtypeCtypeEntry; CtypeError: CtypeError; CtypeEvent: CtypeEvent; CumulusPalletDmpQueueCall: CumulusPalletDmpQueueCall; diff --git a/packages/augment-api/src/interfaces/types-lookup.ts b/packages/augment-api/src/interfaces/types-lookup.ts index 10a93b44d..1395191da 100644 --- a/packages/augment-api/src/interfaces/types-lookup.ts +++ b/packages/augment-api/src/interfaces/types-lookup.ts @@ -704,7 +704,9 @@ declare module '@polkadot/types/lookup' { interface CtypeEvent extends Enum { readonly isCTypeCreated: boolean; readonly asCTypeCreated: ITuple<[AccountId32, H256]>; - readonly type: 'CTypeCreated'; + readonly isCTypeUpdated: boolean; + readonly asCTypeUpdated: H256; + readonly type: 'CTypeCreated' | 'CTypeUpdated'; } /** @name AttestationEvent (60) */ @@ -783,7 +785,7 @@ declare module '@polkadot/types/lookup' { } /** @name PalletDidLookupAccountAccountId20 (68) */ - interface PalletDidLookupAccountAccountId20 extends U8aFixed {} + interface PalletDidLookupAccountAccountId20 extends U8aFixed { } /** @name PalletWeb3NamesEvent (70) */ interface PalletWeb3NamesEvent extends Enum { @@ -832,7 +834,7 @@ declare module '@polkadot/types/lookup' { } /** @name RuntimeCommonAssetsAssetDid (74) */ - interface RuntimeCommonAssetsAssetDid extends KiltAssetDidsV1AssetDid {} + interface RuntimeCommonAssetsAssetDid extends KiltAssetDidsV1AssetDid { } /** @name KiltAssetDidsV1AssetDid (75) */ interface KiltAssetDidsV1AssetDid extends Struct { @@ -856,7 +858,7 @@ declare module '@polkadot/types/lookup' { } /** @name KiltAssetDidsChainV1GenesisHexHash32Reference (78) */ - interface KiltAssetDidsChainV1GenesisHexHash32Reference extends U8aFixed {} + interface KiltAssetDidsChainV1GenesisHexHash32Reference extends U8aFixed { } /** @name KiltAssetDidsChainV1GenericChainId (82) */ interface KiltAssetDidsChainV1GenericChainId extends Struct { @@ -880,10 +882,10 @@ declare module '@polkadot/types/lookup' { } /** @name KiltAssetDidsAssetV1EvmSmartContractFungibleReference (90) */ - interface KiltAssetDidsAssetV1EvmSmartContractFungibleReference extends U8aFixed {} + interface KiltAssetDidsAssetV1EvmSmartContractFungibleReference extends U8aFixed { } /** @name KiltAssetDidsAssetV1EvmSmartContractNonFungibleReference (91) */ - interface KiltAssetDidsAssetV1EvmSmartContractNonFungibleReference extends ITuple<[KiltAssetDidsAssetV1EvmSmartContractFungibleReference, Option]> {} + interface KiltAssetDidsAssetV1EvmSmartContractNonFungibleReference extends ITuple<[KiltAssetDidsAssetV1EvmSmartContractFungibleReference, Option]> { } /** @name KiltAssetDidsAssetV1GenericAssetId (95) */ interface KiltAssetDidsAssetV1GenericAssetId extends Struct { @@ -1153,7 +1155,7 @@ declare module '@polkadot/types/lookup' { } /** @name XcmV2Xcm (118) */ - interface XcmV2Xcm extends Vec {} + interface XcmV2Xcm extends Vec { } /** @name XcmV2Instruction (120) */ interface XcmV2Instruction extends Enum { @@ -1276,7 +1278,7 @@ declare module '@polkadot/types/lookup' { } /** @name XcmV1MultiassetMultiAssets (121) */ - interface XcmV1MultiassetMultiAssets extends Vec {} + interface XcmV1MultiassetMultiAssets extends Vec { } /** @name XcmV1MultiAsset (123) */ interface XcmV1MultiAsset extends Struct { @@ -1800,10 +1802,10 @@ declare module '@polkadot/types/lookup' { } /** @name SpConsensusAuraSr25519AppSr25519Public (188) */ - interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {} + interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public { } /** @name SpCoreSr25519Public (189) */ - interface SpCoreSr25519Public extends U8aFixed {} + interface SpCoreSr25519Public extends U8aFixed { } /** @name SpiritnetRuntimeSessionKeys (194) */ interface SpiritnetRuntimeSessionKeys extends Struct { @@ -1811,7 +1813,7 @@ declare module '@polkadot/types/lookup' { } /** @name SpCoreCryptoKeyTypeId (197) */ - interface SpCoreCryptoKeyTypeId extends U8aFixed {} + interface SpCoreCryptoKeyTypeId extends U8aFixed { } /** @name PalletSessionCall (198) */ interface PalletSessionCall extends Enum { @@ -1863,7 +1865,7 @@ declare module '@polkadot/types/lookup' { } /** @name ParachainStakingSetOrderedSet (204) */ - interface ParachainStakingSetOrderedSet extends Vec {} + interface ParachainStakingSetOrderedSet extends Vec { } /** @name ParachainStakingCandidateStatus (207) */ interface ParachainStakingCandidateStatus extends Enum { @@ -2113,7 +2115,7 @@ declare module '@polkadot/types/lookup' { } /** @name PalletDemocracyVotePriorLock (241) */ - interface PalletDemocracyVotePriorLock extends ITuple<[u64, u128]> {} + interface PalletDemocracyVotePriorLock extends ITuple<[u64, u128]> { } /** @name PalletDemocracyConviction (242) */ interface PalletDemocracyConviction extends Enum { @@ -2670,7 +2672,12 @@ declare module '@polkadot/types/lookup' { readonly asAdd: { readonly ctype: Bytes; } & Struct; - readonly type: 'Add'; + readonly isSetBlockNumber: boolean; + readonly asSetBlockNumber: { + readonly ctypeHash: H256; + readonly blockNumber: u64; + } & Struct; + readonly type: 'Add' | 'SetBlockNumber'; } /** @name AttestationCall (277) */ @@ -2776,13 +2783,13 @@ declare module '@polkadot/types/lookup' { } /** @name SpCoreEd25519Signature (284) */ - interface SpCoreEd25519Signature extends U8aFixed {} + interface SpCoreEd25519Signature extends U8aFixed { } /** @name SpCoreSr25519Signature (286) */ - interface SpCoreSr25519Signature extends U8aFixed {} + interface SpCoreSr25519Signature extends U8aFixed { } /** @name SpCoreEcdsaSignature (287) */ - interface SpCoreEcdsaSignature extends U8aFixed {} + interface SpCoreEcdsaSignature extends U8aFixed { } /** @name DidCall (289) */ interface DidCall extends Enum { @@ -2872,10 +2879,10 @@ declare module '@polkadot/types/lookup' { } /** @name SpCoreEd25519Public (297) */ - interface SpCoreEd25519Public extends U8aFixed {} + interface SpCoreEd25519Public extends U8aFixed { } /** @name SpCoreEcdsaPublic (298) */ - interface SpCoreEcdsaPublic extends U8aFixed {} + interface SpCoreEcdsaPublic extends U8aFixed { } /** @name DidServiceEndpointsDidEndpoint (301) */ interface DidServiceEndpointsDidEndpoint extends Struct { @@ -2942,7 +2949,7 @@ declare module '@polkadot/types/lookup' { } /** @name PalletDidLookupAccountEthereumSignature (313) */ - interface PalletDidLookupAccountEthereumSignature extends SpCoreEcdsaSignature {} + interface PalletDidLookupAccountEthereumSignature extends SpCoreEcdsaSignature { } /** @name PalletWeb3NamesCall (314) */ interface PalletWeb3NamesCall extends Enum { @@ -3052,6 +3059,12 @@ declare module '@polkadot/types/lookup' { readonly maxPovSize: u32; } + /** @name CtypeCtypeEntry (322) */ + interface CtypeCtypeEntry extends Struct { + readonly creator: AccountId32; + readonly createdAt: u64; + } + /** @name SpTrieStorageProof (323) */ interface SpTrieStorageProof extends Struct { readonly trieNodes: BTreeSet; @@ -3475,7 +3488,7 @@ declare module '@polkadot/types/lookup' { } /** @name FrameSupportPalletId (369) */ - interface FrameSupportPalletId extends U8aFixed {} + interface FrameSupportPalletId extends U8aFixed { } /** @name PalletTreasuryError (370) */ interface PalletTreasuryError extends Enum { @@ -3965,12 +3978,12 @@ declare module '@polkadot/types/lookup' { type FrameSystemExtensionsCheckGenesis = Null; /** @name FrameSystemExtensionsCheckNonce (472) */ - interface FrameSystemExtensionsCheckNonce extends Compact {} + interface FrameSystemExtensionsCheckNonce extends Compact { } /** @name FrameSystemExtensionsCheckWeight (473) */ type FrameSystemExtensionsCheckWeight = Null; /** @name PalletTransactionPaymentChargeTransactionPayment (474) */ - interface PalletTransactionPaymentChargeTransactionPayment extends Compact {} + interface PalletTransactionPaymentChargeTransactionPayment extends Compact { } } // declare module diff --git a/packages/core/src/__integrationtests__/Ctypes.spec.ts b/packages/core/src/__integrationtests__/Ctypes.spec.ts index d2256de2e..abfea8699 100644 --- a/packages/core/src/__integrationtests__/Ctypes.spec.ts +++ b/packages/core/src/__integrationtests__/Ctypes.spec.ts @@ -16,7 +16,7 @@ import { KeyTool, makeSigningKeyTool, } from '@kiltprotocol/testing' -import { Crypto } from '@kiltprotocol/utils' +import { Crypto, UUID } from '@kiltprotocol/utils' import { ApiPromise } from '@polkadot/api' import * as CType from '../ctype' import { disconnect } from '../kilt' @@ -30,12 +30,10 @@ beforeAll(async () => { describe('When there is an CtypeCreator and a verifier', () => { let ctypeCreator: DidDocument let paymentAccount: KiltKeyringPair - let ctypeCounter = 0 let key: KeyTool function makeCType(): ICType { - ctypeCounter += 1 - return CType.fromProperties(`ctype${ctypeCounter}`, { + return CType.fromProperties(`Ctype ${UUID.generate()}`, { name: { type: 'string' }, }) } @@ -58,6 +56,7 @@ describe('When there is an CtypeCreator and a verifier', () => { ) await expect(submitTx(authorizedStoreTx, keypair)).rejects.toThrowError() await expect(CType.verifyStored(ctype)).rejects.toThrow() + await expect(CType.fetchFromChain(ctype.$id)).rejects.toThrow() }, 20_000) it('should be possible to create a claim type', async () => { @@ -71,10 +70,12 @@ describe('When there is an CtypeCreator and a verifier', () => { ) await submitTx(authorizedStoreTx, paymentAccount) - expect( - CType.fromChain(await api.query.ctype.ctypes(CType.idToChain(ctype.$id))) - ).toBe(ctypeCreator.uri) - await expect(CType.verifyStored(ctype)).resolves.not.toThrow() + const retrievedCType = await CType.fetchFromChain(ctype.$id) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { createdAt, creator, ...originalCtype } = retrievedCType + expect(originalCtype).toStrictEqual(ctype) + expect(creator).toBe(ctypeCreator.uri) + await expect(CType.verifyStored(retrievedCType)).resolves.not.toThrow() }, 40_000) it('should not be possible to create a claim type that exists', async () => { @@ -99,9 +100,8 @@ describe('When there is an CtypeCreator and a verifier', () => { submitTx(authorizedStoreTx2, paymentAccount) ).rejects.toMatchObject({ section: 'ctype', name: 'CTypeAlreadyExists' }) - expect( - CType.fromChain(await api.query.ctype.ctypes(CType.idToChain(ctype.$id))) - ).toBe(ctypeCreator.uri) + const retrievedCType = await CType.fetchFromChain(ctype.$id) + expect(retrievedCType.creator).toBe(ctypeCreator.uri) }, 45_000) it('should tell when a ctype is not on chain', async () => { @@ -110,9 +110,7 @@ describe('When there is an CtypeCreator and a verifier', () => { }) await expect(CType.verifyStored(iAmNotThere)).rejects.toThrow() - expect( - (await api.query.ctype.ctypes(CType.idToChain(iAmNotThere.$id))).isNone - ).toBe(true) + await expect(CType.fetchFromChain(iAmNotThere.$id)).rejects.toThrow() const fakeHash = Crypto.hashStr('abcdefg') expect((await api.query.ctype.ctypes(fakeHash)).isNone).toBe(true) diff --git a/packages/core/src/__integrationtests__/PublicCredentials.spec.ts b/packages/core/src/__integrationtests__/PublicCredentials.spec.ts index f1e25ac40..0b4f221a5 100644 --- a/packages/core/src/__integrationtests__/PublicCredentials.spec.ts +++ b/packages/core/src/__integrationtests__/PublicCredentials.spec.ts @@ -99,9 +99,8 @@ describe('When there is an attester and ctype NFT name', () => { ) expect(publicCredentialEntry.isSome).toBe(true) - const completeCredential = await PublicCredential.credentialFromChain( - credentialId, - publicCredentialEntry + const completeCredential = await PublicCredential.fetchCredentialFromChain( + credentialId ) // Verify that the retrieved credential matches the input one, plus the generated ID and the attester DID. @@ -126,8 +125,8 @@ describe('When there is an attester and ctype NFT name', () => { } await issueCredential(latestCredential) - const assetCredentials = await PublicCredential.credentialsFromChain( - await api.call.publicCredentials.getBySubject(assetId, null) + const assetCredentials = await PublicCredential.fetchCredentialsFromChain( + assetId ) // We only check that we return two credentials back. @@ -158,8 +157,8 @@ describe('When there is an attester and ctype NFT name', () => { }) await submitTx(authorizedBatch, tokenHolder) - const assetCredentials = await PublicCredential.credentialsFromChain( - await api.call.publicCredentials.getBySubject(assetId, null) + const assetCredentials = await PublicCredential.fetchCredentialsFromChain( + assetId ) // We don't check the content of each credential but only the number of credentials that is returned. @@ -171,17 +170,11 @@ describe('When there is an attester and ctype NFT name', () => { latestCredential, attester.uri ) - let encodedAssetCredential = await api.call.publicCredentials.getById( + let assetCredential = await PublicCredential.fetchCredentialFromChain( credentialId ) - let assetCredential = await PublicCredential.credentialFromChain( - credentialId, - encodedAssetCredential - ) - let encodedAllAssetCredentials = - await api.call.publicCredentials.getBySubject(assetId, null) const allAssetCredentialsBeforeRevocation = - await PublicCredential.credentialsFromChain(encodedAllAssetCredentials) + await PublicCredential.fetchCredentialsFromChain(assetId) // Verify that credential was not revoked before revocation expect(assetCredential.revoked).toBe(false) const revocationTx = api.tx.publicCredentials.revoke(credentialId, null) @@ -193,43 +186,29 @@ describe('When there is an attester and ctype NFT name', () => { ) await submitTx(authorizedTx, tokenHolder) - encodedAssetCredential = await api.call.publicCredentials.getById( + assetCredential = await PublicCredential.fetchCredentialFromChain( credentialId ) - assetCredential = await PublicCredential.credentialFromChain( - credentialId, - encodedAssetCredential - ) - encodedAllAssetCredentials = await api.call.publicCredentials.getBySubject( - assetId, - null - ) const allAssetCredentialsAfterRevocation = - await PublicCredential.credentialsFromChain(encodedAllAssetCredentials) + await PublicCredential.fetchCredentialsFromChain(assetId) expect(assetCredential.revoked).toBe(true) // Verify the number of credentials has not changed after revocation expect(allAssetCredentialsBeforeRevocation.length).toEqual( allAssetCredentialsAfterRevocation.length ) - }) + }, 60_000) it('should be possible to unrevoke a credential', async () => { const credentialId = PublicCredential.getIdForCredential( latestCredential, attester.uri ) - let encodedAssetCredential = await api.call.publicCredentials.getById( + let assetCredential = await PublicCredential.fetchCredentialFromChain( credentialId ) - let assetCredential = await PublicCredential.credentialFromChain( - credentialId, - encodedAssetCredential - ) - let encodedAllAssetCredentials = - await api.call.publicCredentials.getBySubject(assetId, null) const allAssetCredentialsBeforeRevocation = - await PublicCredential.credentialsFromChain(encodedAllAssetCredentials) + await PublicCredential.fetchCredentialsFromChain(assetId) // Verify that credential was revoked before un-revocation expect(assetCredential.revoked).toBe(true) @@ -241,20 +220,11 @@ describe('When there is an attester and ctype NFT name', () => { tokenHolder.address ) await submitTx(authorizedTx, tokenHolder) - - encodedAssetCredential = await api.call.publicCredentials.getById( + assetCredential = await PublicCredential.fetchCredentialFromChain( credentialId ) - assetCredential = await PublicCredential.credentialFromChain( - credentialId, - encodedAssetCredential - ) - encodedAllAssetCredentials = await api.call.publicCredentials.getBySubject( - assetId, - null - ) const allAssetCredentialsAfterRevocation = - await PublicCredential.credentialsFromChain(encodedAllAssetCredentials) + await PublicCredential.fetchCredentialsFromChain(assetId) // Verify it is now not revoked anymore expect(assetCredential.revoked).toBe(false) @@ -262,7 +232,7 @@ describe('When there is an attester and ctype NFT name', () => { expect(allAssetCredentialsBeforeRevocation.length).toEqual( allAssetCredentialsAfterRevocation.length ) - }) + }, 60_000) it('should be possible to remove a credential', async () => { const credentialId = PublicCredential.getIdForCredential( @@ -272,10 +242,8 @@ describe('When there is an attester and ctype NFT name', () => { let encodedAssetCredential = await api.call.publicCredentials.getById( credentialId ) - let encodedAllAssetCredentials = - await api.call.publicCredentials.getBySubject(assetId, null) const allAssetCredentialsBeforeRevocation = - await PublicCredential.credentialsFromChain(encodedAllAssetCredentials) + await PublicCredential.fetchCredentialsFromChain(assetId) // Verify that credential existed before removal expect(encodedAssetCredential.isNone).toBe(false) @@ -291,12 +259,8 @@ describe('When there is an attester and ctype NFT name', () => { encodedAssetCredential = await api.call.publicCredentials.getById( credentialId ) - encodedAllAssetCredentials = await api.call.publicCredentials.getBySubject( - assetId, - null - ) const allAssetCredentialsAfterRevocation = - await PublicCredential.credentialsFromChain(encodedAllAssetCredentials) + await PublicCredential.fetchCredentialsFromChain(assetId) // Verify it is now removed expect(encodedAssetCredential.isNone).toBe(true) @@ -304,7 +268,7 @@ describe('When there is an attester and ctype NFT name', () => { expect(allAssetCredentialsAfterRevocation.length).toEqual( allAssetCredentialsBeforeRevocation.length - 1 ) - }) + }, 60_000) }) describe('When there is an issued public credential', () => { @@ -325,10 +289,7 @@ describe('When there is an issued public credential', () => { latestCredential, attester.uri ) - credential = await PublicCredential.credentialFromChain( - credentialId, - await api.call.publicCredentials.getById(credentialId) - ) + credential = await PublicCredential.fetchCredentialFromChain(credentialId) }) it('should be successfully verified when another party receives it', async () => { @@ -560,13 +521,8 @@ describe('When there is a batch which contains a credential creation', () => { }) it('should correctly parse the block and retrieve the original credentials', async () => { - const encodedCredentials = await api.call.publicCredentials.getBySubject( - assetId, - null - ) - const retrievedCredentials = await PublicCredential.credentialsFromChain( - encodedCredentials - ) + const retrievedCredentials = + await PublicCredential.fetchCredentialsFromChain(assetId) expect(retrievedCredentials.length).toEqual(3) await expect( PublicCredential.verifyCredential(retrievedCredentials[0]) diff --git a/packages/core/src/ctype/CType.chain.ts b/packages/core/src/ctype/CType.chain.ts index cc85510b2..8b8d54078 100644 --- a/packages/core/src/ctype/CType.chain.ts +++ b/packages/core/src/ctype/CType.chain.ts @@ -5,13 +5,26 @@ * found in the LICENSE file in the root directory of this source tree. */ -import type { Option } from '@polkadot/types' -import type { AccountId } from '@polkadot/types/interfaces' +import type { ApiPromise } from '@polkadot/api' +import type { Bytes, GenericCall, Option } from '@polkadot/types' +import type { Call } from '@polkadot/types/interfaces' +import type { BN } from '@polkadot/util' +import type { CtypeCtypeEntry } from '@kiltprotocol/augment-api' import type { CTypeHash, DidUri, ICType } from '@kiltprotocol/types' + +import { ConfigService } from '@kiltprotocol/config' import * as Did from '@kiltprotocol/did' +import { SDKErrors } from '@kiltprotocol/utils' -import { idToHash, serializeForHash } from './CType.js' +import { + getHashForSchema, + hashToId, + idToHash, + serializeForHash, + verifyDataStructure, +} from './CType.js' +import { flattenCalls, isBatch, retrieveExtrinsicFromBlock } from '../utils.js' /** * Encodes the provided CType for use in `api.tx.ctype.add()`. @@ -33,12 +46,166 @@ export function idToChain(cTypeId: ICType['$id']): CTypeHash { return idToHash(cTypeId) } +// Transform a blockchain-formatted CType input (represented as Bytes) into the original [[ICType]]. +// It throws if what was written on the chain was garbage. +function cTypeInputFromChain(input: Bytes): ICType { + try { + // Throws on invalid JSON input. CType is expected to be a valid JSON document. + const reconstructedObject = JSON.parse(input.toUtf8()) + // Re-compute the ID to validate the resulting ICType. + const reconstructedCTypeId = hashToId(getHashForSchema(reconstructedObject)) + const reconstructedCType: ICType = { + ...reconstructedObject, + $id: reconstructedCTypeId, + } + // If throws if the input was a valid JSON but not a valid CType. + verifyDataStructure(reconstructedCType) + return reconstructedCType + } catch (cause) { + throw new SDKErrors.CTypeError( + `The provided payload cannot be parsed as a CType: ${input.toHuman()}`, + { cause } + ) + } +} + +/** + * The details of a CType that are stored on chain. + */ +export interface CTypeChainDetails { + /** + * The DID of the CType's creator. + */ + creator: DidUri + /** + * The block number in which the CType was created. + */ + createdAt: BN +} + +export type ICTypeDetails = ICType & CTypeChainDetails + /** - * Decodes the owner DID from the return value of `api.query.ctype.ctypes(ctypeHash)`. + * Decodes the CType details returned by `api.query.ctype.ctypes()`. * * @param encoded The data from the blockchain. - * @returns The owner DID. + * @returns The decoded data. + */ +export function fromChain(encoded: Option): CTypeChainDetails { + const { creator, createdAt } = encoded.unwrap() + return { + creator: Did.fromChain(creator), + createdAt: createdAt.toBn(), + } +} + +// Given a (nested) call, flattens them and filter by calls that are of type `api.tx.ctype.add`. +function extractCTypeCreationCallsFromDidCall( + api: ApiPromise, + call: Call +): Array> { + const extrinsicCalls = flattenCalls(api, call) + return extrinsicCalls.filter( + (c): c is GenericCall => + api.tx.ctype.add.is(c) + ) +} + +// Given a (nested) call, flattens them and filter by calls that are of type `api.tx.did.submitDidCall`. +function extractDidCallsFromBatchCall( + api: ApiPromise, + call: Call +): Array> { + const extrinsicCalls = flattenCalls(api, call) + return extrinsicCalls.filter( + (c): c is GenericCall => + api.tx.did.submitDidCall.is(c) + ) +} + +/** + * Resolves a CType identifier to the CType definition by fetching data from the block containing the transaction that registered the CType on chain. + * + * @param cTypeId CType ID to use for the query. It is required to complement the information stored on the blockchain in a [[CtypeCtypeEntry]]. + * + * @returns The [[ICTypeDetails]] as the result of combining the on-chain information and the information present in the tx history. */ -export function fromChain(encoded: Option): DidUri { - return Did.fromChain(encoded.unwrap()) +export async function fetchFromChain( + cTypeId: ICType['$id'] +): Promise { + const api = ConfigService.get('api') + const cTypeHash = idToHash(cTypeId) + + const cTypeEntry = await api.query.ctype.ctypes(cTypeHash) + const { createdAt } = fromChain(cTypeEntry) + + const extrinsic = await retrieveExtrinsicFromBlock( + api, + createdAt, + ({ events }) => + events.some( + (event) => + api.events.ctype.CTypeCreated.is(event) && + event.data[1].toString() === cTypeHash + ) + ) + + if (extrinsic === null) { + throw new SDKErrors.CTypeError( + `There is not CType with the provided ID "${cTypeId}" on chain.` + ) + } + + if (!isBatch(api, extrinsic) && !api.tx.did.submitDidCall.is(extrinsic)) { + throw new SDKErrors.PublicCredentialError( + 'Extrinsic should be either a `did.submitDidCall` extrinsic or a batch with at least a `did.submitDidCall` extrinsic' + ) + } + + // If we're dealing with a batch, flatten any nested `submit_did_call` calls, + // otherwise the extrinsic is itself a submit_did_call, so just take it. + const didCalls = isBatch(api, extrinsic) + ? extrinsic.args[0].flatMap((batchCall) => + extractDidCallsFromBatchCall(api, batchCall) + ) + : [extrinsic] + + // From the list of DID calls, only consider ctype::add calls, bundling each of them with their DID submitter. + // It returns a list of [reconstructedCType, attesterDid]. + const ctypeCallContent = didCalls.flatMap((didCall) => { + const ctypeCreationCalls = extractCTypeCreationCallsFromDidCall( + api, + didCall.args[0].call + ) + // Re-create the issued public credential for each call identified. + return ctypeCreationCalls.map( + (ctypeCreationCall) => + [ + cTypeInputFromChain(ctypeCreationCall.args[0]), + Did.fromChain(didCall.args[0].did), + ] as const + ) + }) + + // If more than a call is present, it always considers the last one as the valid one. + const lastRightCTypeCreationCall = ctypeCallContent + .reverse() + .find((cTypeInput) => { + return cTypeInput[0].$id === cTypeId + }) + + if (!lastRightCTypeCreationCall) { + throw new SDKErrors.CTypeError( + 'Block should always contain the full CType, eventually.' + ) + } + + const [ctypeInput, creator] = lastRightCTypeCreationCall + + return { + ...ctypeInput, + $id: cTypeId, + creator, + createdAt, + } } diff --git a/packages/core/src/publicCredential/PublicCredential.chain.ts b/packages/core/src/publicCredential/PublicCredential.chain.ts index a9ccec276..cc4488892 100644 --- a/packages/core/src/publicCredential/PublicCredential.chain.ts +++ b/packages/core/src/publicCredential/PublicCredential.chain.ts @@ -5,19 +5,19 @@ * found in the LICENSE file in the root directory of this source tree. */ -import type { ApiPromise } from '@polkadot/api' import type { AssetDidUri, CTypeHash, IDelegationNode, IPublicCredentialInput, IPublicCredential, + DidUri, } from '@kiltprotocol/types' -import type { GenericCall, Option, Result, u64, Vec } from '@polkadot/types' -import type { Call, Extrinsic, Hash } from '@polkadot/types/interfaces' -import type { ITuple } from '@polkadot/types/types' +import type { ApiPromise } from '@polkadot/api' +import type { GenericCall, Option } from '@polkadot/types' +import type { Call } from '@polkadot/types/interfaces' +import type { BN } from '@polkadot/util' import type { - PublicCredentialError, PublicCredentialsCredentialsCredential, PublicCredentialsCredentialsCredentialEntry, } from '@kiltprotocol/augment-api' @@ -31,7 +31,7 @@ import { validateUri } from '@kiltprotocol/asset-did' import { SDKErrors } from '@kiltprotocol/utils' import { getIdForCredential } from './PublicCredential.js' -import { isBatch } from '../utils.js' +import { flattenCalls, isBatch, retrieveExtrinsicFromBlock } from '../utils.js' export interface EncodedPublicCredential { ctypeHash: CTypeHash @@ -61,16 +61,6 @@ export function toChain( } } -// Flatten any nested batch calls into a single list of calls. -function flattenCalls(api: ApiPromise, call: Call): Call[] { - if (isBatch(api, call)) { - // Inductive case - return call.args[0].flatMap((c) => flattenCalls(api, c)) - } - // Base case - return [call] -} - // Transform a blockchain-formatted public credential [[PublicCredentialsCredentialsCredential]] into the original [[IPublicCredentialInput]]. // It throws if what was written on the chain was garbage. function credentialInputFromChain({ @@ -89,29 +79,52 @@ function credentialInputFromChain({ } } -// Retrieve a given block and looks into it to find a public credential creation tx that matches the provided credential ID. -async function retrievePublicCredentialCreationExtrinsicFromBlock( - api: ApiPromise, - credentialId: HexString, - blockNumber: u64 -): Promise { - const { extrinsics } = await api.derive.chain.getBlockByNumber(blockNumber) - const successfulExtrinsics = extrinsics.filter( - ({ dispatchError }) => !dispatchError - ) - // If there is more than one (e.g., same credential issued multiple times in the same block) it should not matter since the ID is generated over the content, hence same ID -> same content. - // Nevertheless, take only the last one, if present, as that is for sure what ended up being in the blockchain state. - const lastPublicCredentialCreationExtrinsic = successfulExtrinsics - .reverse() - .find(({ events }) => - events.some( - (event) => - api.events.publicCredentials.CredentialStored.is(event) && - event.data[1].toString() === credentialId - ) - ) +/** + * The details of a public credential that are stored on chain. + */ +export interface PublicCredentialEntry { + /** + * CType hash of the public credential. + */ + ctypeHash: HexString + /** + * DID URI of the attester. + */ + attester: DidUri + /** + * Flag indicating whether the credential is currently revoked. + */ + revoked: boolean + /** + * Issuance block number of the credential. + */ + blockNumber: BN + /** + * Authorization information used by the attester when issuing the credential. + */ + authorizationId: IDelegationNode['id'] | null +} - return lastPublicCredentialCreationExtrinsic?.extrinsic ?? null +/** + * Decodes the public credential details returned by `api.query.publicCredentials.credentials(subjectId)`. + * + * @param encoded The data from the blockchain. + * @returns The decoded data. + */ +export function fromChain( + encoded: Option +): PublicCredentialEntry { + const { attester, authorizationId, blockNumber, ctypeHash, revoked } = + encoded.unwrap() + return { + ctypeHash: ctypeHash.toHex(), + attester: didFromChain(attester), + revoked: revoked.toPrimitive(), + authorizationId: authorizationId.isSome + ? authorizationId.unwrap().toHex() + : null, + blockNumber: blockNumber.toBn(), + } } // Given a (nested) call, flattens them and filter by calls that are of type `api.tx.publicCredentials.add`. @@ -145,21 +158,27 @@ function extractDidCallsFromBatchCall( * Hence, calling `api.call.publicCredentials.getById(credentialId)` and then passing the result to this function is the only way to trust that a credential with a given ID is valid. * * @param credentialId Credential ID to use for the query. It is required to complement the information stored on the blockchain in a [[PublicCredentialsCredentialsCredentialEntry]]. - * @param publicCredentialEntry The raw public credential details from blockchain. * @returns The [[IPublicCredential]] as the result of combining the on-chain information and the information present in the tx history. */ -export async function credentialFromChain( - credentialId: HexString, - publicCredentialEntry: Option +export async function fetchCredentialFromChain( + credentialId: IPublicCredential['id'] ): Promise { const api = ConfigService.get('api') + const publicCredentialEntry = await api.call.publicCredentials.getById( + credentialId + ) const { blockNumber, revoked } = publicCredentialEntry.unwrap() - const extrinsic = await retrievePublicCredentialCreationExtrinsicFromBlock( + const extrinsic = await retrieveExtrinsicFromBlock( api, - credentialId, - blockNumber + blockNumber, + ({ events }) => + events.some( + (event) => + api.events.publicCredentials.CredentialStored.is(event) && + event.data[1].toString() === credentialId + ) ) if (extrinsic === null) { @@ -228,29 +247,25 @@ export async function credentialFromChain( * This is the **only** secure way for users to retrieve and verify all the credentials issued to a given [[AssetDidUri]]. * Hence, calling `api.call.publicCredentials.getBySubject(asset_id)` and then passing the result to this function is the only way to trust that the credentials for a given AssetDID are valid. * - * @param publicCredentialEntries The raw public credential details from blockchain. + * @param subject The AssetDID of the subject. * @returns An array of [[IPublicCredential]] as the result of combining the on-chain information and the information present in the tx history. If the result is an error, it maps it to the right error type. */ -export async function credentialsFromChain( - publicCredentialEntries: Result< - Vec>, - PublicCredentialError - > +export async function fetchCredentialsFromChain( + subject: AssetDidUri ): Promise { + const api = ConfigService.get('api') + + const publicCredentialEntries = await api.call.publicCredentials.getBySubject( + subject, + null + ) if (publicCredentialEntries.isErr) { throw new Error(publicCredentialEntries.asErr.toString()) } - const api = ConfigService.get('api') return Promise.all( - publicCredentialEntries.asOk.map(([encodedId, encodedCredentialEntry]) => - credentialFromChain( - encodedId.toHex(), - api.createType( - 'Option', - encodedCredentialEntry - ) - ) + publicCredentialEntries.asOk.map(([encodedId]) => + fetchCredentialFromChain(encodedId.toHex()) ) ) } diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 5dad44dab..12d3a420d 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -6,9 +6,10 @@ */ import type { ApiPromise } from '@polkadot/api' +import type { TxWithEvent } from '@polkadot/api-derive/types' +import type { GenericCall, GenericExtrinsic } from '@polkadot/types' import type { Call, Extrinsic } from '@polkadot/types/interfaces' - -import { GenericExtrinsic } from '@polkadot/types' +import type { BN } from '@polkadot/util' /** * Checks wheather the provided extrinsic or call represents a batch. @@ -21,14 +22,64 @@ import { GenericExtrinsic } from '@polkadot/types' export function isBatch( api: ApiPromise, extrinsic: Extrinsic | Call -): extrinsic is GenericExtrinsic< - | typeof api.tx.utility.batch.args - | typeof api.tx.utility.batchAll.args - | typeof api.tx.utility.forceBatch.args -> { +): extrinsic is + | GenericExtrinsic< + | typeof api.tx.utility.batch.args + | typeof api.tx.utility.batchAll.args + | typeof api.tx.utility.forceBatch.args + > + | GenericCall< + | typeof api.tx.utility.batch.args + | typeof api.tx.utility.batchAll.args + | typeof api.tx.utility.forceBatch.args + > { return ( api.tx.utility.batch.is(extrinsic) || api.tx.utility.batchAll.is(extrinsic) || api.tx.utility.forceBatch.is(extrinsic) ) } + +/** + * Flatten all calls into a single array following a DFS approach. + * + * For example, given the calls [[N1, N2], [N3, [N4, N5], N6]], the final list will look like [N1, N2, N3, N4, N5, N6]. + * + * @param api The [[ApiPromise]] object. + * @param call The [[Call]] which can potentially contain nested calls. + * + * @returns A list of [[Call]] nested according to the rules above. + */ +export function flattenCalls(api: ApiPromise, call: Call): Call[] { + if (isBatch(api, call)) { + // Inductive case + return call.args[0].flatMap((c) => flattenCalls(api, c)) + } + // Base case + return [call] +} + +/** + * Retrieve the last extrinsic from a block that matches the provided filter. + * + * The function ignores failed extrinsics and, if multiple extrinsics from the block match the provided filter, it only takes the last one. + * + * @param api The [[ApiPromise]] object. + * @param blockNumber The number of the block to parse. + * @param filter The filter to apply to the transactions in the block. + * + * @returns The last extrinsic in the block matching the filter, or null if no extrinsic is found. + */ +export async function retrieveExtrinsicFromBlock( + api: ApiPromise, + blockNumber: BN, + filter: (tx: TxWithEvent) => boolean +): Promise { + const { extrinsics } = await api.derive.chain.getBlockByNumber(blockNumber) + const successfulExtrinsics = extrinsics.filter( + ({ dispatchError }) => !dispatchError + ) + const extrinsicLastOccurrence = successfulExtrinsics.reverse().find(filter) + + return extrinsicLastOccurrence?.extrinsic ?? null +} diff --git a/packages/utils/src/SDKErrors.ts b/packages/utils/src/SDKErrors.ts index a5ebe4295..e13e13b67 100644 --- a/packages/utils/src/SDKErrors.ts +++ b/packages/utils/src/SDKErrors.ts @@ -25,6 +25,8 @@ export class UnauthorizedError extends SDKError {} export class CTypeHashMissingError extends SDKError {} +export class CTypeError extends SDKError {} + export class CTypeIdMismatchError extends SDKError { constructor(fromSchema: string, provided: string) { super(