diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef62c9ee6..de47dd8f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ changes.
- Add PDF_API_URL to the frontend config [Issue 1575](https://github.com/IntersectMBO/govtool/issues/1575)
- Provide DB-Sync Postgres envs to the backend config
- Add redirects to cards on Home after user connects [Issue 1442](https://github.com/IntersectMBO/govtool/issues/1442)
+- Add support for CIP-100 for context to Governance Action Vote [Issue 1582](https://github.com/IntersectMBO/govtool/issues/1582)
### Fixed
diff --git a/govtool/frontend/src/components/organisms/VoteContext/VoteContextStoringInformation.tsx b/govtool/frontend/src/components/organisms/VoteContext/VoteContextStoringInformation.tsx
index 95485fcd1..d5a583f37 100644
--- a/govtool/frontend/src/components/organisms/VoteContext/VoteContextStoringInformation.tsx
+++ b/govtool/frontend/src/components/organisms/VoteContext/VoteContextStoringInformation.tsx
@@ -31,7 +31,7 @@ export const VoteContextStoringInformation = ({
validateURL,
watch,
generateMetadata,
- onClickDownloadFile,
+ onClickDownloadJson,
} = useVoteContextForm(setSavedHash, setStep, setErrorMessage);
const openGuideAboutStoringInformation = () =>
@@ -78,7 +78,7 @@ export const VoteContextStoringInformation = ({
}
sx={{
diff --git a/govtool/frontend/src/consts/CIP100Context.ts b/govtool/frontend/src/consts/CIP100Context.ts
new file mode 100644
index 000000000..2cfa2127f
--- /dev/null
+++ b/govtool/frontend/src/consts/CIP100Context.ts
@@ -0,0 +1,51 @@
+export const CIP_100_CONTEXT = {
+ "@language": "en-us",
+ CIP100:
+ "https://github.com/cardano-foundation/CIPs/blob/master/CIP-0100/README.md#",
+ hashAlgorithm: "CIP100:hashAlgorithm",
+ body: {
+ "@id": "CIP100:body",
+ "@context": {
+ references: {
+ "@id": "CIP100:references",
+ "@container": "@set" as const,
+ "@context": {
+ GovernanceMetadata: "CIP100:GovernanceMetadataReference",
+ Other: "CIP100:OtherReference",
+ label: "CIP100:reference-label",
+ uri: "CIP100:reference-uri",
+ referenceHash: {
+ "@id": "CIP100:referenceHash",
+ "@context": {
+ hashDigest: "CIP100:hashDigest",
+ hashAlgorithm: "CIP100:hashAlgorithm",
+ },
+ },
+ },
+ },
+ comment: "CIP100:comment",
+ externalUpdates: {
+ "@id": "CIP100:externalUpdates",
+ "@context": {
+ title: "CIP100:update-title",
+ uri: "CIP100:uri",
+ },
+ },
+ },
+ },
+ authors: {
+ "@id": "CIP100:authors",
+ "@container": "@set" as const,
+ "@context": {
+ name: "http://xmlns.com/foaf/0.1/name",
+ witness: {
+ "@id": "CIP100:witness",
+ "@context": {
+ witnessAlgorithm: "CIP100:witnessAlgorithm",
+ publicKey: "CIP100:publicKey",
+ signature: "CIP100:signature",
+ },
+ },
+ },
+ },
+};
diff --git a/govtool/frontend/src/consts/index.ts b/govtool/frontend/src/consts/index.ts
index e4e44f2d5..e1e7275f0 100644
--- a/govtool/frontend/src/consts/index.ts
+++ b/govtool/frontend/src/consts/index.ts
@@ -1,7 +1,8 @@
-export * from "./externalDataModalConfig";
+export * from "./CIP100Context";
export * from "./colors";
export * from "./dRepActions";
export * from "./dRepDirectory";
+export * from "./externalDataModalConfig";
export * from "./governanceAction";
export * from "./icons";
export * from "./images";
diff --git a/govtool/frontend/src/context/governanceAction.test.tsx b/govtool/frontend/src/context/governanceAction.test.tsx
index f32cd30ee..87d4d2835 100644
--- a/govtool/frontend/src/context/governanceAction.test.tsx
+++ b/govtool/frontend/src/context/governanceAction.test.tsx
@@ -92,7 +92,7 @@ describe("GovernanceActionProvider", () => {
const hash = await createHash(jsonld!);
expect(hash).toBeDefined();
expect(hash).toBe(
- "5eebd2c216f3e0718283eb8c0c9ac27ba0a1fd04a7ca849b8c739fb546311931",
+ "b84e9890a34d6e59a983cf8f695214162893de5fc5310b12b75f4fe3dab0d7ab",
);
};
test();
diff --git a/govtool/frontend/src/context/governanceAction.tsx b/govtool/frontend/src/context/governanceAction.tsx
index a1f1066e2..807a22608 100644
--- a/govtool/frontend/src/context/governanceAction.tsx
+++ b/govtool/frontend/src/context/governanceAction.tsx
@@ -11,7 +11,7 @@ import { blake2bHex } from "blakejs";
import * as Sentry from "@sentry/react";
import { CIP_108, GOVERNANCE_ACTION_CONTEXT } from "@/consts";
-import { canonizeJSON, generateJsonld, generateMetadataBody } from "@/utils";
+import { generateJsonld, generateMetadataBody } from "@/utils";
type GovActionMetadata = {
title: string;
@@ -76,9 +76,8 @@ const GovernanceActionProvider = ({ children }: PropsWithChildren) => {
*/
const createHash = useCallback(async (jsonLD: NodeObject) => {
try {
- const canonizedJson = await canonizeJSON(jsonLD);
- const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32);
- return canonizedJsonHash;
+ const jsonHash = blake2bHex(JSON.stringify(jsonLD), undefined, 32);
+ return jsonHash;
} catch (error) {
Sentry.captureException(error);
console.error(error);
diff --git a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts
index 9fdaae96f..02eea1a1f 100644
--- a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts
+++ b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts
@@ -14,7 +14,6 @@ import {
} from "@consts";
import { useCardano, useModal } from "@context";
import {
- canonizeJSON,
correctAdaFormat,
downloadJson,
generateJsonld,
@@ -109,11 +108,10 @@ export const useCreateGovernanceActionForm = (
const jsonld = await generateJsonld(body, GOVERNANCE_ACTION_CONTEXT);
- const canonizedJson = await canonizeJSON(jsonld);
- const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32);
+ const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32);
// That allows to validate metadata hash
- setHash(canonizedJsonHash);
+ setHash(jsonHash);
setJson(jsonld);
return jsonld;
diff --git a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts
index 3ea4e1872..12f5e964b 100644
--- a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts
+++ b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts
@@ -13,12 +13,7 @@ import {
storageInformationErrorModals,
} from "@consts";
import { useCardano, useModal } from "@context";
-import {
- canonizeJSON,
- downloadJson,
- generateJsonld,
- generateMetadataBody,
-} from "@utils";
+import { downloadJson, generateJsonld, generateMetadataBody } from "@utils";
import { MetadataStandard, MetadataValidationStatus } from "@models";
import { useWalletErrorModal } from "@hooks";
import { useValidateMutation } from "../mutations";
@@ -97,10 +92,9 @@ export const useEditDRepInfoForm = (
const jsonld = await generateJsonld(body, DREP_CONTEXT, CIP_QQQ);
- const canonizedJson = await canonizeJSON(jsonld);
- const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32);
+ const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32);
- setHash(canonizedJsonHash);
+ setHash(jsonHash);
setJson(jsonld);
return jsonld;
diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx
index 39c590d95..4a08d2520 100644
--- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx
+++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx
@@ -15,7 +15,6 @@ import {
import { useCardano, useModal } from "@context";
import { MetadataStandard, MetadataValidationStatus } from "@models";
import {
- canonizeJSON,
downloadJson,
ellipsizeText,
generateJsonld,
@@ -106,10 +105,9 @@ export const useRegisterAsdRepForm = (
const jsonld = await generateJsonld(body, DREP_CONTEXT, CIP_QQQ);
- const canonizedJson = await canonizeJSON(jsonld);
- const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32);
+ const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32);
- setHash(canonizedJsonHash);
+ setHash(jsonHash);
setJson(jsonld);
return jsonld;
diff --git a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx
index e94e80a79..25fc2ef24 100644
--- a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx
+++ b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx
@@ -2,9 +2,11 @@ import { Dispatch, SetStateAction, useCallback, useState } from "react";
import { useFormContext } from "react-hook-form";
import { blake2bHex } from "blakejs";
import * as Sentry from "@sentry/react";
+import { NodeObject } from "jsonld";
-import { downloadTextFile } from "@utils";
+import { downloadJson, generateJsonld, generateMetadataBody } from "@utils";
import { MetadataValidationStatus } from "@models";
+import { CIP_100, CIP_100_CONTEXT } from "@/consts";
import { useValidateMutation } from "../mutations";
@@ -21,6 +23,7 @@ export const useVoteContextForm = (
) => {
const { validateMetadata } = useValidateMutation();
const [hash, setHash] = useState(null);
+ const [json, setJson] = useState(null);
const {
control,
@@ -35,20 +38,28 @@ export const useVoteContextForm = (
const generateMetadata = useCallback(async () => {
const { voteContextText } = getValues();
+ const body = generateMetadataBody({
+ data: {
+ comment: voteContextText,
+ },
+ acceptedKeys: ["comment"],
+ standardReference: CIP_100,
+ });
+ const jsonld = await generateJsonld(body, CIP_100_CONTEXT, CIP_100);
- const canonizedJsonHash = blake2bHex(voteContextText, undefined, 32);
+ const jsonHash = blake2bHex(JSON.stringify(jsonld), undefined, 32);
// That allows to validate metadata hash
- setHash(canonizedJsonHash);
+ setHash(jsonHash);
+ setJson(jsonld);
- return voteContextText;
+ return jsonld;
}, [getValues]);
- const onClickDownloadFile = useCallback(() => {
- const { voteContextText } = getValues();
- if (!voteContextText) return;
- downloadTextFile(voteContextText, "Vote_Context");
- }, [getValues]);
+ const onClickDownloadJson = () => {
+ if (!json) return;
+ downloadJson(json, "Vote_Context");
+ };
const validateHash = useCallback(
async (url: string, localHash: string | null) => {
@@ -102,7 +113,7 @@ export const useVoteContextForm = (
generateMetadata,
getValues,
isValid,
- onClickDownloadFile,
+ onClickDownloadJson,
register,
reset,
setValue,
diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts
index b50bff7d6..9d85b6baf 100644
--- a/govtool/frontend/src/i18n/locales/en.ts
+++ b/govtool/frontend/src/i18n/locales/en.ts
@@ -434,7 +434,7 @@ export const en = {
viewOtherDetails: "View other details",
viewProposalDetails: "View proposal details",
vote: "Vote",
- voteContextFileName: "Vote_Context.txt",
+ voteContextFileName: "Vote_Context.jsonld",
votedOnByMe: "Voted on by me",
voteOnGovActions: "Vote on Governance Action",
voteSubmitted: "Vote submitted",
diff --git a/govtool/metadata-validation/src/app.service.test.ts b/govtool/metadata-validation/src/app.service.test.ts
index 0e3d8f946..8b7fb402f 100644
--- a/govtool/metadata-validation/src/app.service.test.ts
+++ b/govtool/metadata-validation/src/app.service.test.ts
@@ -7,7 +7,7 @@ import { AppService } from './app.service';
import { ValidateMetadataDTO } from '@dto';
import { MetadataValidationStatus } from '@enums';
import { MetadataStandard } from '@types';
-import { canonizeJSON, validateMetadataStandard, parseMetadata } from '@utils';
+import { validateMetadataStandard, parseMetadata } from '@utils';
import { AxiosResponse, AxiosRequestHeaders } from 'axios';
jest.mock('@utils');
@@ -42,7 +42,6 @@ describe('AppService', () => {
body: 'testBody',
headers: {},
};
- const canonizedMetadata = 'canonizedMetadata';
const parsedMetadata = { parsed: 'metadata' };
const response: AxiosResponse = {
data,
@@ -57,7 +56,6 @@ describe('AppService', () => {
jest.spyOn(httpService, 'get').mockReturnValueOnce(of(response));
(validateMetadataStandard as jest.Mock).mockResolvedValueOnce(undefined);
(parseMetadata as jest.Mock).mockReturnValueOnce(parsedMetadata);
- (canonizeJSON as jest.Mock).mockResolvedValueOnce(canonizedMetadata);
jest.spyOn(blake, 'blake2bHex').mockReturnValueOnce(hash);
const result = await service.validateMetadata(validateMetadataDTO);
@@ -69,7 +67,6 @@ describe('AppService', () => {
});
expect(validateMetadataStandard).toHaveBeenCalledWith(data, standard);
expect(parseMetadata).toHaveBeenCalledWith(data.body, standard);
- expect(canonizeJSON).toHaveBeenCalledWith(data);
});
it('should handle URL_NOT_FOUND error', async () => {
@@ -100,7 +97,6 @@ describe('AppService', () => {
const data = {
body: 'testBody',
};
- const canonizedMetadata = 'canonizedMetadata';
const parsedMetadata = { parsed: 'metadata' };
const response: AxiosResponse = {
@@ -116,7 +112,6 @@ describe('AppService', () => {
jest.spyOn(httpService, 'get').mockReturnValueOnce(of(response));
(validateMetadataStandard as jest.Mock).mockResolvedValueOnce(undefined);
(parseMetadata as jest.Mock).mockReturnValueOnce(parsedMetadata);
- (canonizeJSON as jest.Mock).mockResolvedValueOnce(canonizedMetadata);
jest.spyOn(blake, 'blake2bHex').mockReturnValueOnce('differentHash');
const result = await service.validateMetadata(validateMetadataDTO);
@@ -151,7 +146,6 @@ describe('AppService', () => {
jest.spyOn(httpService, 'get').mockReturnValueOnce(of(response));
(validateMetadataStandard as jest.Mock).mockResolvedValueOnce(undefined);
(parseMetadata as jest.Mock).mockReturnValueOnce(parsedMetadata);
- (canonizeJSON as jest.Mock).mockRejectedValueOnce(new Error());
const result = await service.validateMetadata(validateMetadataDTO);
diff --git a/govtool/metadata-validation/src/app.service.ts b/govtool/metadata-validation/src/app.service.ts
index 633d77f2a..6775e9d3f 100644
--- a/govtool/metadata-validation/src/app.service.ts
+++ b/govtool/metadata-validation/src/app.service.ts
@@ -5,7 +5,7 @@ import * as blake from 'blakejs';
import { ValidateMetadataDTO } from '@dto';
import { LoggerMessage, MetadataValidationStatus } from '@enums';
-import { canonizeJSON, validateMetadataStandard, parseMetadata } from '@utils';
+import { validateMetadataStandard, parseMetadata } from '@utils';
import { MetadataStandard, ValidateMetadataResult } from '@types';
@Injectable()
@@ -40,17 +40,8 @@ export class AppService {
metadata = parseMetadata(data.body, standard);
}
- let canonizedMetadata;
- if (!noStandard) {
- try {
- canonizedMetadata = await canonizeJSON(data);
- } catch (error) {
- throw MetadataValidationStatus.INVALID_JSONLD;
- }
- }
-
const hashedMetadata = blake.blake2bHex(
- !standard ? data : canonizedMetadata,
+ !standard ? data : metadata,
undefined,
32,
);