Skip to content

Commit 1e72115

Browse files
committed
feat(standard-contracts): pin compiled artifacts for release-branch reproducibility check
1 parent d17d039 commit 1e72115

7 files changed

Lines changed: 112 additions & 0 deletions

File tree

yarn-project/.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ noir-contracts.js/**/*.json
55
noir-contracts.js/src
66
noir-test-contracts.js/**/*.json
77
noir-test-contracts.js/src
8+
standard-contracts/src/**/pinned/*.json
89
noir-protocol-circuits-types/src/target/*
910
*.md
1011
end-to-end/src/fixtures/dumps/*.json

yarn-project/standard-contracts/src/auth-registry/pinned/AuthRegistry.artifact.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Release-branch reproducibility gate for the auth_registry contract.
2+
//
3+
// Symmetric to the freshness gate in `derive_auth_registry.test.ts`: that test asserts the
4+
// committed `.nr` stamp / TS twin stay aligned with the freshly-built artifact. This test goes
5+
// one level further and pins the *full artifact JSON* so a release branch can detect any
6+
// unintended bytecode drift between the pinned release artifact and the current build output.
7+
//
8+
// TODO: enable on release branches. Run manually with:
9+
// yarn workspace @aztec/standard-contracts test src/auth-registry/reproducibility.test.ts
10+
// and then drop the `it.skip` once the release branch is cut.
11+
import { createHash } from 'node:crypto';
12+
import { promises as fs } from 'node:fs';
13+
import path from 'node:path';
14+
import { fileURLToPath } from 'node:url';
15+
16+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
17+
const ARTIFACT_PATH = path.join(
18+
__dirname,
19+
'../../../../noir-projects/noir-contracts/target/auth_registry_contract-AuthRegistry.json',
20+
);
21+
const PINNED_ARTIFACT_PATH = path.join(__dirname, 'pinned/AuthRegistry.artifact.json');
22+
23+
const REGEN_HINT =
24+
'auth_registry pinned artifact is stale; if this drift is intentional on a release branch, copy ' +
25+
`${ARTIFACT_PATH} over ${PINNED_ARTIFACT_PATH} and commit the result.`;
26+
27+
describe('auth_registry artifact reproducibility', () => {
28+
it.skip('committed pinned artifact matches the freshly-built artifact byte-for-byte', async () => {
29+
const [pinned, fresh] = await Promise.all([fs.readFile(PINNED_ARTIFACT_PATH), fs.readFile(ARTIFACT_PATH)]);
30+
const pinnedHash = createHash('sha256').update(pinned).digest('hex');
31+
const freshHash = createHash('sha256').update(fresh).digest('hex');
32+
if (pinnedHash !== freshHash) {
33+
throw new Error(`${REGEN_HINT}\n pinned sha256 = ${pinnedHash}\n fresh sha256 = ${freshHash}`);
34+
}
35+
});
36+
});

yarn-project/standard-contracts/src/multi-call-entrypoint/pinned/MultiCallEntrypoint.artifact.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Release-branch reproducibility gate for the multi_call_entrypoint contract.
2+
//
3+
// Symmetric to the freshness gate in `derive_multi_call_entrypoint.test.ts`: that test asserts
4+
// the committed TS twin stays aligned with the freshly-built artifact. This test goes one level
5+
// further and pins the *full artifact JSON* so a release branch can detect any unintended
6+
// bytecode drift between the pinned release artifact and the current build output.
7+
//
8+
// TODO: enable on release branches. Run manually with:
9+
// yarn workspace @aztec/standard-contracts test src/multi-call-entrypoint/reproducibility.test.ts
10+
// and then drop the `it.skip` once the release branch is cut.
11+
import { createHash } from 'node:crypto';
12+
import { promises as fs } from 'node:fs';
13+
import path from 'node:path';
14+
import { fileURLToPath } from 'node:url';
15+
16+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
17+
const ARTIFACT_PATH = path.join(
18+
__dirname,
19+
'../../../../noir-projects/noir-contracts/target/multi_call_entrypoint_contract-MultiCallEntrypoint.json',
20+
);
21+
const PINNED_ARTIFACT_PATH = path.join(__dirname, 'pinned/MultiCallEntrypoint.artifact.json');
22+
23+
const REGEN_HINT =
24+
'multi_call_entrypoint pinned artifact is stale; if this drift is intentional on a release branch, copy ' +
25+
`${ARTIFACT_PATH} over ${PINNED_ARTIFACT_PATH} and commit the result.`;
26+
27+
describe('multi_call_entrypoint artifact reproducibility', () => {
28+
it.skip('committed pinned artifact matches the freshly-built artifact byte-for-byte', async () => {
29+
const [pinned, fresh] = await Promise.all([fs.readFile(PINNED_ARTIFACT_PATH), fs.readFile(ARTIFACT_PATH)]);
30+
const pinnedHash = createHash('sha256').update(pinned).digest('hex');
31+
const freshHash = createHash('sha256').update(fresh).digest('hex');
32+
if (pinnedHash !== freshHash) {
33+
throw new Error(`${REGEN_HINT}\n pinned sha256 = ${pinnedHash}\n fresh sha256 = ${freshHash}`);
34+
}
35+
});
36+
});

yarn-project/standard-contracts/src/public-checks/pinned/PublicChecks.artifact.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Release-branch reproducibility gate for the public_checks contract.
2+
//
3+
// Symmetric to the freshness gate in `derive_public_checks.test.ts`: that test asserts the
4+
// committed `.nr` stamp / TS twin stay aligned with the freshly-built artifact. This test goes
5+
// one level further and pins the *full artifact JSON* so a release branch can detect any
6+
// unintended bytecode drift between the pinned release artifact and the current build output.
7+
//
8+
// TODO: enable on release branches. Run manually with:
9+
// yarn workspace @aztec/standard-contracts test src/public-checks/reproducibility.test.ts
10+
// and then drop the `it.skip` once the release branch is cut.
11+
import { createHash } from 'node:crypto';
12+
import { promises as fs } from 'node:fs';
13+
import path from 'node:path';
14+
import { fileURLToPath } from 'node:url';
15+
16+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
17+
const ARTIFACT_PATH = path.join(
18+
__dirname,
19+
'../../../../noir-projects/noir-contracts/target/public_checks_contract-PublicChecks.json',
20+
);
21+
const PINNED_ARTIFACT_PATH = path.join(__dirname, 'pinned/PublicChecks.artifact.json');
22+
23+
const REGEN_HINT =
24+
'public_checks pinned artifact is stale; if this drift is intentional on a release branch, copy ' +
25+
`${ARTIFACT_PATH} over ${PINNED_ARTIFACT_PATH} and commit the result.`;
26+
27+
describe('public_checks artifact reproducibility', () => {
28+
it.skip('committed pinned artifact matches the freshly-built artifact byte-for-byte', async () => {
29+
const [pinned, fresh] = await Promise.all([fs.readFile(PINNED_ARTIFACT_PATH), fs.readFile(ARTIFACT_PATH)]);
30+
const pinnedHash = createHash('sha256').update(pinned).digest('hex');
31+
const freshHash = createHash('sha256').update(fresh).digest('hex');
32+
if (pinnedHash !== freshHash) {
33+
throw new Error(`${REGEN_HINT}\n pinned sha256 = ${pinnedHash}\n fresh sha256 = ${freshHash}`);
34+
}
35+
});
36+
});

0 commit comments

Comments
 (0)