diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts index fcbacad077f4..26a76f04c6b7 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -1,7 +1,10 @@ import { type ContractArtifact } from '@aztec/foundation/abi'; -import { getTestContractArtifact } from '../tests/fixtures.js'; import { computeArtifactHash } from './artifact_hash.js'; +import { getPathToFixture, getTestContractArtifact } from '../tests/fixtures.js'; +import { loadContractArtifact } from '@aztec/types/abi'; +import type { NoirCompiledContract } from '@aztec/types/noir'; +import { readFileSync } from 'fs'; describe('ArtifactHash', () => { it('calculates the artifact hash', () => { @@ -17,7 +20,7 @@ describe('ArtifactHash', () => { notes: {}, }; expect(computeArtifactHash(emptyArtifact).toString()).toMatchInlineSnapshot( - `"0x0c6fd9b48570721c5d36f978d084d77cacbfd2814f1344985f40e62bea6e61be"`, + `"0x0dea64e7fa0688017f77bcb7075485485afb4a5f1f8508483398869439f82fdf"`, ); }); @@ -26,8 +29,20 @@ describe('ArtifactHash', () => { for (let i = 0; i < 1000; i++) { expect(computeArtifactHash(testArtifact).toString()).toMatchInlineSnapshot( - `"0x16b7b028aaaa06d648cc521c385644d7786d2f1ae49157b27a424b172b298162"`, + `"0x0daa596e37c780de679311a601bd8b371c40d5a6898f452a06180bfef69e7df8"`, ); } }); + + it('calculates the test contract artifact hash', () => { + const path = getPathToFixture('Test.test.json'); + const content = JSON.parse(readFileSync(path).toString()) as NoirCompiledContract; + content.outputs.structs.functions.reverse(); + + const testArtifact = loadContractArtifact(content); + + expect(computeArtifactHash(testArtifact).toString()).toMatchInlineSnapshot( + `"0x0daa596e37c780de679311a601bd8b371c40d5a6898f452a06180bfef69e7df8"`, + ); + }); }); diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.ts b/yarn-project/circuits.js/src/contract/artifact_hash.ts index de8ada1782cc..de23fe9594dd 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.ts @@ -59,11 +59,7 @@ export function computeArtifactHashPreimage(artifact: ContractArtifact) { } export function computeArtifactMetadataHash(artifact: ContractArtifact) { - // TODO: #6021 We need to make sure the artifact is deterministic from any specific compiler run. This relates to selectors not being sorted and being - // apparently random in the order they appear after compiled w/ nargo. We can try to sort this upon loading an artifact. - // TODO: #6021: Should we use the sorted event selectors instead? They'd need to be unique for that. - // Response - The output selectors need to be sorted, because if not noir makes no guarantees on the order of outputs for some reason - return sha256Fr(Buffer.from(JSON.stringify({ name: artifact.name }), 'utf-8')); + return sha256Fr(Buffer.from(JSON.stringify({ name: artifact.name, outputs: artifact.outputs }), 'utf-8')); } export function computeArtifactFunctionTreeRoot(artifact: ContractArtifact, fnType: FunctionType) { diff --git a/yarn-project/circuits.js/src/tests/fixtures.ts b/yarn-project/circuits.js/src/tests/fixtures.ts index ee8146a79fe1..280c4240bb58 100644 --- a/yarn-project/circuits.js/src/tests/fixtures.ts +++ b/yarn-project/circuits.js/src/tests/fixtures.ts @@ -48,6 +48,6 @@ export function getSampleUnconstrainedFunctionBroadcastedEventPayload(): Buffer return Buffer.from(readFileSync(path).toString(), 'hex'); } -function getPathToFixture(name: string) { +export function getPathToFixture(name: string) { return resolve(dirname(fileURLToPath(import.meta.url)), `../../fixtures/${name}`); } diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index b58d5cc55279..c8698596c397 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -358,7 +358,16 @@ export const ContractArtifactSchema: ZodFor = z.object({ aztecNrVersion: z.string().optional(), functions: z.array(FunctionArtifactSchema), outputs: z.object({ - structs: z.record(z.array(AbiTypeSchema)), + structs: z.record(z.array(AbiTypeSchema)).transform(structs => { + for (const [key, value] of Object.entries(structs)) { + // We are manually ordering events and functions in the abi by path. + // The path ordering is arbitrary, and only needed to ensure deterministic order. + if (key === 'events' || key === 'functions') { + structs[key] = (value as StructType[]).sort((a, b) => a.path > b.path ? -1 : 1); + } + } + return structs; + }), globals: z.record(z.array(AbiValueSchema)), }), storageLayout: z.record(z.object({ slot: schemas.Fr })),