Skip to content
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
17 changes: 4 additions & 13 deletions release-image/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,10 @@ FROM aztecprotocol/release-image-base
# Copy in project files.
COPY . /usr/src

# We do a short (~3 second) forge build of 3 stubborn files to make sure the cache has all entries.
# This saves time when users make a local network.
# This is impossibly finicky to get right from outside the container, due to having a different set of files.
RUN cd /usr/src/l1-contracts && \
forge build && \
# Remove test files from the cache that cause warnings.
cache="cache/solidity-files-cache.json" && \
jq '.files |= with_entries(select(.key | endswith(".t.sol") | not))' "$cache" > "$cache.tmp" && mv "$cache.tmp" "$cache"

# Even after that, we want to make sure that forge COULD force a rebuild for FOUNDRY_PROFILE=production.
# This is used in the prod deploy. As well, --broadcast writes to l1-contracts/broadcast.
RUN mkdir -p /usr/src/l1-contracts/broadcast && \
chmod -R a+rwX /usr/src/l1-contracts/out /usr/src/l1-contracts/cache /usr/src/l1-contracts/broadcast
# L1 contracts artifacts are pre-built in yarn-project/l1-artifacts/foundry-artifacts.
# Ensure directories are writable for forge --broadcast at runtime.
RUN mkdir -p /usr/src/yarn-project/l1-artifacts/foundry-artifacts/broadcast && \
chmod -R a+rwX /usr/src/yarn-project/l1-artifacts/foundry-artifacts

ARG BUILD_METADATA=""
ENV BUILD_METADATA=$BUILD_METADATA
Expand Down
33 changes: 2 additions & 31 deletions release-image/Dockerfile.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,5 @@
!/yarn-project/protocol-contracts/artifacts/
!/yarn-project/noir-contracts.js/artifacts/
!/yarn-project/simulator/artifacts/

### Start foundry / l1-contracts files

# There's a bunch of complexity here because foundry is very particular.
# We want to take our full build from the repo, copy it over, and avoid any
# recompilation or warnings from forge inside the docker container.

# We need to to run l1-contracts deploy scripts (script/deploy/*)
# All source/test files needed so forge cache entries match and avoid recompilation
!/l1-contracts/foundry.toml
# We have to include the solc binary annoyingly, even if we aim to fully have a cached build.
!/l1-contracts/solc-*
!/l1-contracts/script/deploy
!/l1-contracts/src/

# Needed to prevent rebuilds.
!/l1-contracts/cache/
!/l1-contracts/out/

# Remove test files, except for shouting.t.sol that for an undiagnosed reason causes a warning if not present.
/l1-contracts/out/**/*.t.sol
!/l1-contracts/test/shouting.t.sol

# Honk verifier.
!/l1-contracts/generated/

# The minimum needed to run our solidity dependencies.
!/l1-contracts/lib/**/remappings.txt
!/l1-contracts/lib/**/foundry.toml
!/l1-contracts/lib/**/*.sol
### End foundry / l1-contracts files
# This includes all files needed for l1-contracts deployment via forge script.
!/yarn-project/l1-artifacts/l1-contracts/
73 changes: 60 additions & 13 deletions yarn-project/ethereum/src/deploy_aztec_l1_contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { fileURLToPath } from '@aztec/foundation/url';
import { bn254 } from '@noble/curves/bn254';
import type { Abi, Narrow } from 'abitype';
import { spawn } from 'child_process';
import { dirname, resolve } from 'path';
import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync } from 'fs';
import { dirname, join, resolve } from 'path';
import readline from 'readline';
import type { Hex } from 'viem';
import { foundry, mainnet, sepolia } from 'viem/chains';
Expand Down Expand Up @@ -107,17 +108,66 @@ export interface ValidatorJson {
}

/**
* Gets the path to the l1-contracts directory.
* Gets the path to the l1-contracts foundry artifacts directory.
* These are copied from l1-contracts to yarn-project/l1-artifacts/l1-contracts
* during build to make yarn-project self-contained.
*/
export function getL1ContractsPath(): string {
// Try to find l1-contracts relative to this file
const currentDir = dirname(fileURLToPath(import.meta.url));

// Go up from yarn-project/ethereum/src to yarn-project, then to repo root, then to l1-contracts
const l1ContractsPath = resolve(currentDir, '..', '..', '..', 'l1-contracts');
// Go up from yarn-project/ethereum/dest to yarn-project, then to l1-artifacts/l1-contracts
const l1ContractsPath = resolve(currentDir, '..', '..', 'l1-artifacts', 'l1-contracts');
return l1ContractsPath;
}

// Cached deployment directory
let preparedDeployDir: string | undefined;

function cleanupDeployDir() {
if (preparedDeployDir) {
try {
rmSync(preparedDeployDir, { recursive: true, force: true });
} catch {
// ignore cleanup errors
}
preparedDeployDir = undefined;
}
}

/**
* Prepares a temp directory for forge deployment.
* Copies all artifacts with preserved timestamps (required for forge cache validity).
* A fresh broadcast/ directory is created for deployment outputs.
*/
export function prepareL1ContractsForDeployment(): string {
if (preparedDeployDir && existsSync(preparedDeployDir)) {
return preparedDeployDir;
}

const basePath = getL1ContractsPath();
const tempDir = mkdtempSync(join(dirname(basePath), '.foundry-deploy-'));
preparedDeployDir = tempDir;
process.on('exit', cleanupDeployDir);

// Copy all dirs with preserved timestamps (required for forge cache validity)
const copyOpts = { recursive: true, preserveTimestamps: true };
cpSync(join(basePath, 'out'), join(tempDir, 'out'), copyOpts);
cpSync(join(basePath, 'lib'), join(tempDir, 'lib'), copyOpts);
cpSync(join(basePath, 'cache'), join(tempDir, 'cache'), copyOpts);
cpSync(join(basePath, 'src'), join(tempDir, 'src'), copyOpts);
cpSync(join(basePath, 'script'), join(tempDir, 'script'), copyOpts);
cpSync(join(basePath, 'generated'), join(tempDir, 'generated'), copyOpts);
cpSync(join(basePath, 'foundry.toml'), join(tempDir, 'foundry.toml'));
cpSync(join(basePath, 'foundry.lock'), join(tempDir, 'foundry.lock'));
for (const file of readdirSync(basePath)) {
if (file.startsWith('solc-')) {
cpSync(join(basePath, file), join(tempDir, file));
}
}

mkdirSync(join(tempDir, 'broadcast'));
return tempDir;
}

/**
* Computes the validator data for passing to Solidity.
* Only computes the G2 public key (which requires scalar multiplication on G2, not available in EVM).
Expand Down Expand Up @@ -211,7 +261,6 @@ export async function deployAztecL1Contracts(
'Initial validator funding requires minting tokens, which is not possible with an external token.',
);
}
const currentDir = dirname(fileURLToPath(import.meta.url));
const chain = createEthereumChain([rpcUrl], chainId);

const l1Client = createExtendedL1Client([rpcUrl], privateKey, chain.chainInfo);
Expand Down Expand Up @@ -240,8 +289,8 @@ export async function deployAztecL1Contracts(
}
}

// Relative location of l1-contracts in monorepo or docker image.
const l1ContractsPath = resolve(currentDir, '..', '..', '..', 'l1-contracts');
// Use foundry-artifacts from l1-artifacts package
const l1ContractsPath = prepareL1ContractsForDeployment();

const FORGE_SCRIPT = 'script/deploy/DeployAztecL1Contracts.s.sol';
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
Expand Down Expand Up @@ -513,10 +562,8 @@ export const deployRollupForUpgrade = async (
| 'zkPassportArgs'
>,
) => {
const currentDir = dirname(fileURLToPath(import.meta.url));

// Relative location of l1-contracts in monorepo or docker image.
const l1ContractsPath = resolve(currentDir, '..', '..', '..', 'l1-contracts');
// Use foundry-artifacts from l1-artifacts package
const l1ContractsPath = prepareL1ContractsForDeployment();

const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
Expand Down
1 change: 1 addition & 0 deletions yarn-project/l1-artifacts/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
src
l1-contracts
6 changes: 4 additions & 2 deletions yarn-project/l1-artifacts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"clean": "rm -rf ./dest ./generated .tsbuildinfo",
"formatting": "run -T prettier --check ./generated && run -T eslint ./generated",
"formatting:fix": "run -T prettier -w ./generated",
"generate": "yarn generate:l1-contracts",
"copy-artifacts": "bash scripts/copy-foundry-artifacts.sh",
"generate": "yarn copy-artifacts && yarn generate:l1-contracts",
"generate:l1-contracts": "bash scripts/generate-artifacts.sh"
},
"dependencies": {
Expand All @@ -30,7 +31,8 @@
},
"files": [
"dest",
"generated"
"generated",
"l1-contracts"
],
"types": "./dest/index.d.ts"
}
29 changes: 29 additions & 0 deletions yarn-project/l1-artifacts/scripts/copy-foundry-artifacts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail

# Copies select Foundry artifacts from l1-contracts to yarn-project/l1-artifacts/l1-contracts.
# This makes yarn-project self-contained for runtime contract deployment.
#
# We use cp -p to preserve timestamps - forge cache uses timestamps to detect changes.
# See release-image/Dockerfile.dockerignore for the canonical list of what's needed.

cd $(git rev-parse --show-toplevel)/yarn-project/l1-artifacts

src="../../l1-contracts"

[ -d "$src/out" ] || { echo "Error: l1-contracts/out not found. Build l1-contracts first."; exit 1; }

rm -rf "l1-contracts"
mkdir -p "l1-contracts/script" "l1-contracts/lib" "l1-contracts/broadcast"

# Copy build artifacts, cache, sources, and config (preserving timestamps for cache validity)
cp -rp "$src"/{out,cache,src,generated} "l1-contracts/"
cp -rp "$src/script/deploy" "l1-contracts/script/" # only deploy/, other scripts depend on test files
cp -p "$src"/{foundry.toml,foundry.lock,solc-*} "l1-contracts/"
abs_dest=$(pwd)/l1-contracts
# Keep only the foundry relevant files from lib
(cd "$src" && find lib \( -name "*.sol" -o -name "remappings.txt" -o -name "foundry.toml" \) -exec cp --parents -t "$abs_dest" {} +)

# Foundry is very finicky about copying out subsets.
# Patch over what foundry feels needs to be rebuild (~3 seconds on mainframe)
(cd "l1-contracts" && forge build)
Loading