Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

entries: Add support for verification of registry entries #255

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions demo/src/dedi/registry-entries-tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,21 @@ async function main() {
);

console.log('\n✅ Registry Entry reinstated!', registryEntryReinstate);

console.log(`\n❄️ Registry Entry verification `)

const verificationResult = await Cord.Entries.verifyAgainstInputProperties(
registryEntry,
updateEntryDigest,
`did:cord:3${authorIdentity.address}`,
registry.uri
)

if (verificationResult.isValid) {
console.log(`✅ Verification successful! "${registryEntry}" 🎉`)
} else {
console.log(`🚫 Verification failed! - "${verificationResult.message}" 🚫`)
}
}

main()
Expand Down
155 changes: 155 additions & 0 deletions packages/entries/src/Entries.chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
*/
import {
SDKErrors,
DecoderUtils,
} from '@cord.network/utils';


Expand All @@ -49,10 +50,15 @@ import {
CordKeyringPair,
Option,
RegistryAuthorizationUri,
IRegistryEntryChainStorage,
RegistryUri,
DidUri
} from '@cord.network/types';

import { Chain } from '@cord.network/network';

import { encodeAddress } from '@polkadot/util-crypto';

import { ConfigService } from '@cord.network/config'

import type {
Expand All @@ -62,6 +68,7 @@ import type {
import {
uriToIdentifier,
uriToEntryIdAndDigest,
identifierToUri,
} from '@cord.network/identifier'

export async function isRegistryEntryStored(
Expand Down Expand Up @@ -340,3 +347,151 @@ export async function dispatchReinstateEntryToChain(
)
}
}


/**
* Decodes the registry entry details from the blockchain state.
* This function takes an optional encoded entry and an identifier,
* then extracts and formats the relevant properties into a structured object.
*
* @param {Option<PalletEntriesRegistryEntryDetails>} encoded -
* The optional encoded data from the blockchain representing the registry entry details.
* It may contain the entry details or be `None`.
*
* @param {string} identifier -
* The identifier used to generate the URI for the registry entry.
*
* @returns {IRegistryEntryChainStorage | null}
* - Returns an object containing the decoded registry entry details structured as `IRegistryEntryChainStorage`.
* - If the encoded data is `None`, returns `null`.
*
* @example
* // Example Usage:
* const encodedEntryDetails = ... // fetched from the blockchain
* const identifier = "someIdentifier";
*
* const registryEntry = decodeRegistryEntryDetailsFromChain(encodedEntryDetails, identifier);
* console.log(registryEntry); // Outputs the decoded registry entry details.
*
*/
export function decodeRegistryEntryDetailsFromChain(
encoded: Option<PalletEntriesRegistryEntryDetails>,
identifier: string
): IRegistryEntryChainStorage | null {
if (encoded.isNone) {
return null;
}

const chainRegistryEntry = encoded.unwrap();

/*
* Below code block encodes the data from the chain present in raw
* to its respective formats.
*/
const registryEntry: IRegistryEntryChainStorage = {
uri: identifierToUri(identifier) as EntryUri,
digest: chainRegistryEntry.digest.toHex(),
revoked: chainRegistryEntry.revoked.valueOf(),
creatorUri: `did:cord:3${encodeAddress(chainRegistryEntry.creator, 29)}` as DidUri,
registryUri: identifierToUri(
DecoderUtils.hexToString(chainRegistryEntry.registryId.toString())
) as RegistryUri
};

console.log("chainRegistryEntry after", registryEntry);

return registryEntry;
}


/**
* Retrieves the details of a registry entry from the blockchain using the provided identifier.
* This asynchronous function queries the blockchain for the registry entry associated with
* the specified identifier and decodes the details into a structured format.
*
* @param {string} identifier -
* The identifier used to query the registry entry from the blockchain.
*
* @returns {Promise<IRegistryEntryChainStorage | null>}
* - Returns a promise that resolves to an object containing the decoded registry entry details
* structured as `IRegistryEntryChainStorage`.
* - If no entry is found, it throws an error.
*
* @throws {SDKErrors.CordFetchError}
* Throws an error if there is no registry entry associated with the provided identifier.
*
* @example
* // Example Usage:
* const identifier = "someIdentifier";
*
* try {
* const entryDetails = await getDetailsfromChain(identifier);
* console.log(entryDetails); // Outputs the registry entry details.
* } catch (error) {
* console.error(error.message); // Handle the error accordingly.
* }
*
*/
export async function getDetailsfromChain(
identifier: string
): Promise<IRegistryEntryChainStorage | null> {
const api = ConfigService.get('api');
const registryEntryId = uriToIdentifier(identifier);

const registryEntry = await api.query.entries.registryEntries(registryEntryId);

const decodedDetails = decodeRegistryEntryDetailsFromChain(registryEntry, identifier);

if (!decodedDetails) {
throw new SDKErrors.CordFetchError(
`There is no registry entry with the provided ID "${registryEntryId}" present on the chain.`
);
}

return decodedDetails;
}


/**
* Fetches the registry entry details from the blockchain using the specified entry URI.
* This asynchronous function converts the entry URI into its corresponding identifier,
* retrieves the details of the registry entry from the blockchain, and returns them in a
* structured format.
*
* @param {EntryUri} registryEntryUri -
* The URI of the registry entry for which details are to be fetched.
*
* @returns {Promise<IRegistryEntryChainStorage>}
* - Returns a promise that resolves to an object containing the decoded registry entry details
* structured as `IRegistryEntryChainStorage`.
*
* @throws {SDKErrors.CordFetchError}
* Throws an error if no registry entry is found associated with the provided URI.
*
* @example
* // Example Usage:
* const registryEntryUri = "someEntryUri";
*
* try {
* const entryDetails = await fetchRegistryEntryDetailsFromChain(registryEntryUri);
* console.log(entryDetails); // Outputs the registry entry details.
* } catch (error) {
* console.error(error.message); // Handle the error accordingly.
* }
*
*/
export async function fetchRegistryEntryDetailsFromChain(
registryEntryUri: EntryUri
): Promise<IRegistryEntryChainStorage> {
const registryEntryObj = uriToEntryIdAndDigest(registryEntryUri);

const entryDetails = await getDetailsfromChain(registryEntryObj.identifier);

if (!entryDetails) {
throw new SDKErrors.CordFetchError(
`There is no registry entry with the provided ID "${registryEntryObj.identifier}" present on the chain.`
);
}

return entryDetails;
}
127 changes: 123 additions & 4 deletions packages/entries/src/Entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,25 @@ import {
blake2AsHex,
} from "@cord.network/types";

import { SDKErrors } from '@cord.network/utils';

import { DataUtils } from '@cord.network/utils'

import {
SDKErrors,
DataUtils,
} from '@cord.network/utils';
fetchRegistryEntryDetailsFromChain
} from "./Entries.chain";

import {
uriToIdentifier,
updateRegistryEntryUri,
buildRegistryEntryUri,
checkIdentifier,
identifierToUri,
uriToEntryIdAndDigest
} from '@cord.network/identifier';

import { ConfigService } from '@cord.network/config';


/**
* Generates a URI for a registry entry based on its digest, registry URI, and creator's address.
*
Expand Down Expand Up @@ -375,3 +379,118 @@ export async function updateEntriesProperties(

return registryEntryObj
}


/**
* Verifies the input properties of a registry entry URI against its corresponding chain state details.
* This function ensures that the provided digest, creator URI, and registry URI match the data retrieved
* from the blockchain. It also checks if the entry is revoked or if the URIs are mismatched.
*
* @param {EntryUri} registryEntryUri - The element's URI to be verified against the chain's registry entry.
* It should be of the form `entry:cord:IdDigest:Digest`.
* @param {HexString} digest - The expected digest (hash) associated with the registry entry.
* @param {DidUri} [creatorUri] - (Optional) The expected DID of the creator of the registry entry.
* @param {RegistryUri} [registryUri] - (Optional) The expected registry URI associated with the registry entry.
*
* @returns {Promise<{ isValid: boolean; message: string }>}
* - A result object containing:
* - `isValid`: A boolean indicating whether the verification was successful.
* - `message`: A message describing the outcome of the verification.
*
* @throws {Error} - If an unexpected error occurs during the verification process.
*
* @example
* // Example Usage:
* const registryEntryUri = "cord:3xyz123" as EntryUri;
* const digest = "0xabcdef..." as HexString;
* const creatorUri = "did:cord:3F7HsGqJPyz..." as DidUri;
* const registryUri = "cord:registry:xyz" as RegistryUri;
*
* const result = await verifyAgainstInputProperties(
* registryEntryUri,
* digest,
* creatorUri,
* registryUri
* );
*
* console.log(result.isValid); // true or false
* console.log(result.message); // 'Digest properties provided are valid and match...'
*
*/
export async function verifyAgainstInputProperties(
registryEntryUri: EntryUri,
digest: HexString,
creatorUri?: DidUri,
registryUri?: RegistryUri,
): Promise<{ isValid: boolean; message: string }> {
try {
const registryEntryStatus = await fetchRegistryEntryDetailsFromChain(registryEntryUri);
const registryEntryObj = uriToEntryIdAndDigest(registryEntryUri);
const entryUri = identifierToUri(registryEntryObj.identifier);

if (!registryEntryStatus) {
return {
isValid: false,
message: `Registry Entry details for "${digest}" not found.`,
}
}

console.log("uri", entryUri, "digest", digest, "creatorUri", creatorUri, "registryUri", registryUri);

if (digest !== registryEntryStatus.digest) {
return {
isValid: false,
message: 'Digest does not match with Registry Entry Digest.',
}
}

if (registryEntryStatus?.revoked) {
return {
isValid: false,
message: `Registry Entry "${registryEntryUri}" Revoked.`,
}
}

if (entryUri !== registryEntryStatus.uri) {
return {
isValid: false,
message: 'Registry Entry and Chain Entry URI details does not match.',
}
}

if (creatorUri) {
if (creatorUri !== registryEntryStatus.creatorUri) {
return {
isValid: false,
message: 'Registry Entry and Digest creator does not match.',
}
}
}

if (registryUri) {
if (registryUri !== registryEntryStatus.registryUri) {
return {
isValid: false,
message: 'Registry Entry and Chain Registry URI does not match.',
}
}
}

return {
isValid: true,
message:
'Digest properties provided are valid and matches the registry entry details.',
}
} catch (error) {
if (error instanceof Error) {
return {
isValid: false,
message: `Error verifying properties: ${error}`,
}
}
return {
isValid: false,
message: 'An unknown error occurred while verifying the properties.',
}
}
}