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 6f3f4b8c9721..0fc093ed44b3 100644 --- a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -1,6 +1,10 @@ import { type ContractArtifact } from '@aztec/foundation/abi'; +import { loadContractArtifact } from '@aztec/types/abi'; +import type { NoirCompiledContract } from '@aztec/types/noir'; -import { getTestContractArtifact } from '../tests/fixtures.js'; +import { readFileSync } from 'fs'; + +import { getPathToFixture, getTestContractArtifact } from '../tests/fixtures.js'; import { computeArtifactHash } from './artifact_hash.js'; describe('ArtifactHash', () => { @@ -17,7 +21,7 @@ describe('ArtifactHash', () => { notes: {}, }; expect(computeArtifactHash(emptyArtifact).toString()).toMatchInlineSnapshot( - `"0x0c6fd9b48570721c5d36f978d084d77cacbfd2814f1344985f40e62bea6e61be"`, + `"0x0dea64e7fa0688017f77bcb7075485485afb4a5f1f8508483398869439f82fdf"`, ); }); @@ -26,8 +30,20 @@ describe('ArtifactHash', () => { for (let i = 0; i < 1000; i++) { expect(computeArtifactHash(testArtifact).toString()).toMatchInlineSnapshot( - `"0x11ba97d2d4de6335cc86d271d3c4a6237840cf630eaa442cf75d1666ff475f61"`, + `"0x237feccc8e34a39c0e5133c8653fc278b39275bfa3f7459e4aba07d53b752c19"`, ); } }); + + 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( + `"0x237feccc8e34a39c0e5133c8653fc278b39275bfa3f7459e4aba07d53b752c19"`, + ); + }); }); 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..2a22565a615e 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 })),