Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Deterministic deployments for L1 #8031

Merged
merged 1 commit into from
Aug 16, 2024
Merged
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
feat: Deterministic deployments for L1
spalladino committed Aug 15, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 263e02b3786d2a1ce5b3cbe80c51aa993f1c2941
5 changes: 3 additions & 2 deletions .github/workflows/devnet-deploys.yml
Original file line number Diff line number Diff line change
@@ -401,6 +401,7 @@ jobs:
--private-key ${{ env.CONTRACT_PUBLISHER_PRIVATE_KEY }} \
--rpc-url https://${{ env.DEPLOY_TAG }}-mainnet-fork.aztec.network:8545/${{ env.API_KEY }} \
--l1-chain-id ${{ env.L1_CHAIN_ID }} \
--salt ${{ github.run_id }} \
--json | tee ./l1_contracts.json

# upload contract addresses to S3
@@ -436,7 +437,7 @@ jobs:
working-directory: ./yarn-project/aztec/terraform/node
run: |
terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/aztec-node"
terraform apply -input=false -auto-approve -replace="aws_efs_file_system.node_data_store" -var="NODE_P2P_TCP_PORT=${{ needs.set-network.outputs.node_tcp_range_start }}" -var="NODE_P2P_UDP_PORT=${{ needs.set-network.outputs.node_udp_range_start }}"
terraform apply -input=false -auto-approve -var="NODE_P2P_TCP_PORT=${{ needs.set-network.outputs.node_tcp_range_start }}" -var="NODE_P2P_UDP_PORT=${{ needs.set-network.outputs.node_udp_range_start }}"

- name: Deploy Aztec Prover Nodes
working-directory: ./yarn-project/aztec/terraform/prover-node
@@ -454,7 +455,7 @@ jobs:
working-directory: ./yarn-project/aztec/terraform/pxe
run: |
terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/pxe"
terraform apply -input=false -auto-approve -replace="aws_efs_file_system.pxe_data_store"
terraform apply -input=false -auto-approve

bootstrap:
runs-on: ubuntu-latest
5 changes: 3 additions & 2 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
@@ -75,8 +75,9 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
IRegistry _registry,
IAvailabilityOracle _availabilityOracle,
IERC20 _fpcJuice,
bytes32 _vkTreeRoot
) Leonidas(msg.sender) {
bytes32 _vkTreeRoot,
address _ares
) Leonidas(_ares) {
verifier = new MockVerifier();
REGISTRY = _registry;
AVAILABILITY_ORACLE = _availabilityOracle;
2 changes: 2 additions & 0 deletions l1-contracts/src/core/interfaces/messagebridge/IRegistry.sol
Original file line number Diff line number Diff line change
@@ -31,4 +31,6 @@ interface IRegistry {
// docs:start:registry_number_of_versions
function numberOfVersions() external view returns (uint256);
// docs:end:registry_number_of_versions

function isRollupRegistered(address _rollup) external view returns (bool);
}
18 changes: 16 additions & 2 deletions l1-contracts/src/core/messagebridge/Registry.sol
Original file line number Diff line number Diff line change
@@ -24,10 +24,10 @@ contract Registry is IRegistry, Ownable {
mapping(uint256 version => DataStructures.RegistrySnapshot snapshot) internal snapshots;
mapping(address rollup => uint256 version) internal rollupToVersion;

constructor() Ownable(msg.sender) {
constructor(address _owner) Ownable(_owner) {
// Inserts a "dead" rollup at version 0
// This is simply done to make first version 1, which fits better with the rest of the system
upgrade(address(0xdead));
_upgrade(address(0xdead));
}

/**
@@ -49,6 +49,16 @@ contract Registry is IRegistry, Ownable {
return version;
}

/**
* @notice Returns whther the rollup is registered
* @param _rollup - The address of the rollup contract
* @return Whether the rollup is registered
*/
function isRollupRegistered(address _rollup) external view override(IRegistry) returns (bool) {
(, bool exists) = _getVersionFor(_rollup);
return exists;
}

/**
* @notice Fetches a snapshot of the registry indicated by `version`
* @dev the version is 0 indexed, so the first snapshot is version 0.
@@ -87,6 +97,10 @@ contract Registry is IRegistry, Ownable {
* @return The version of the new snapshot
*/
function upgrade(address _rollup) public override(IRegistry) onlyOwner returns (uint256) {
return _upgrade(_rollup);
}

function _upgrade(address _rollup) internal returns (uint256) {
(, bool exists) = _getVersionFor(_rollup);
if (exists) revert Errors.Registry__RollupAlreadyRegistered(_rollup);

2 changes: 1 addition & 1 deletion l1-contracts/test/Registry.t.sol
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ contract RegistryTest is Test {
Registry internal registry;

function setUp() public {
registry = new Registry();
registry = new Registry(address(this));
}

function testConstructorSetup() public {
6 changes: 4 additions & 2 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
@@ -51,10 +51,12 @@ contract RollupTest is DecoderBase {
vm.warp(initialTime);
}

registry = new Registry();
registry = new Registry(address(this));
availabilityOracle = new AvailabilityOracle();
portalERC20 = new PortalERC20();
rollup = new Rollup(registry, availabilityOracle, IERC20(address(portalERC20)), bytes32(0));
rollup = new Rollup(
registry, availabilityOracle, IERC20(address(portalERC20)), bytes32(0), address(this)
);
inbox = Inbox(address(rollup.INBOX()));
outbox = Outbox(address(rollup.OUTBOX()));

7 changes: 4 additions & 3 deletions l1-contracts/test/portals/TokenPortal.t.sol
Original file line number Diff line number Diff line change
@@ -59,10 +59,11 @@ contract TokenPortalTest is Test {
uint256 internal l2BlockNumber = 69;

function setUp() public {
registry = new Registry();
registry = new Registry(address(this));
portalERC20 = new PortalERC20();
rollup =
new Rollup(registry, new AvailabilityOracle(), IERC20(address(portalERC20)), bytes32(0));
rollup = new Rollup(
registry, new AvailabilityOracle(), IERC20(address(portalERC20)), bytes32(0), address(this)
);
inbox = rollup.INBOX();
outbox = rollup.OUTBOX();

7 changes: 4 additions & 3 deletions l1-contracts/test/portals/UniswapPortal.t.sol
Original file line number Diff line number Diff line change
@@ -54,10 +54,11 @@ contract UniswapPortalTest is Test {
uint256 forkId = vm.createFork(vm.rpcUrl("mainnet_fork"));
vm.selectFork(forkId);

registry = new Registry();
registry = new Registry(address(this));
PortalERC20 portalERC20 = new PortalERC20();
rollup =
new Rollup(registry, new AvailabilityOracle(), IERC20(address(portalERC20)), bytes32(0));
rollup = new Rollup(
registry, new AvailabilityOracle(), IERC20(address(portalERC20)), bytes32(0), address(this)
);
registry.upgrade(address(rollup));
portalERC20.mint(address(rollup), 1000000);

6 changes: 4 additions & 2 deletions l1-contracts/test/sparta/DevNet.t.sol
Original file line number Diff line number Diff line change
@@ -57,10 +57,12 @@ contract DevNetTest is DecoderBase {
vm.warp(initialTime);
}

registry = new Registry();
registry = new Registry(address(this));
availabilityOracle = new AvailabilityOracle();
portalERC20 = new PortalERC20();
rollup = new Rollup(registry, availabilityOracle, IERC20(address(portalERC20)), bytes32(0));
rollup = new Rollup(
registry, availabilityOracle, IERC20(address(portalERC20)), bytes32(0), address(this)
);
inbox = Inbox(address(rollup.INBOX()));
outbox = Outbox(address(rollup.OUTBOX()));

6 changes: 4 additions & 2 deletions l1-contracts/test/sparta/Sparta.t.sol
Original file line number Diff line number Diff line change
@@ -60,10 +60,12 @@ contract SpartaTest is DecoderBase {
vm.warp(initialTime);
}

registry = new Registry();
registry = new Registry(address(this));
availabilityOracle = new AvailabilityOracle();
portalERC20 = new PortalERC20();
rollup = new Rollup(registry, availabilityOracle, IERC20(address(portalERC20)), bytes32(0));
rollup = new Rollup(
registry, availabilityOracle, IERC20(address(portalERC20)), bytes32(0), address(this)
);
inbox = Inbox(address(rollup.INBOX()));
outbox = Outbox(address(rollup.OUTBOX()));

1 change: 1 addition & 0 deletions yarn-project/aztec/src/cli/cli.ts
Original file line number Diff line number Diff line change
@@ -99,6 +99,7 @@ export function injectAztecCommands(program: Command, userLog: LogFn, debugLogge
process.exit(1);
}
}

installSignalHandlers(debugLogger.info, signalHandlers);

if (services.length) {
10 changes: 3 additions & 7 deletions yarn-project/aztec/src/cli/cmds/start_archiver.ts
Original file line number Diff line number Diff line change
@@ -7,8 +7,7 @@ import {
} from '@aztec/archiver';
import { createDebugLogger } from '@aztec/aztec.js';
import { type ServerList } from '@aztec/foundation/json-rpc/server';
import { AztecLmdbStore } from '@aztec/kv-store/lmdb';
import { initStoreForRollup } from '@aztec/kv-store/utils';
import { createStore } from '@aztec/kv-store/utils';
import {
createAndStartTelemetryClient,
getConfigEnvVars as getTelemetryClientConfig,
@@ -22,11 +21,8 @@ export const startArchiver = async (options: any, signalHandlers: (() => Promise
const archiverConfig = extractRelevantOptions<ArchiverConfig>(options, archiverConfigMappings);

const storeLog = createDebugLogger('aztec:archiver:lmdb');
const store = await initStoreForRollup(
AztecLmdbStore.open(archiverConfig.dataDirectory, false),
archiverConfig.l1Contracts.rollupAddress,
storeLog,
);
const rollupAddress = archiverConfig.l1Contracts.rollupAddress;
const store = await createStore(archiverConfig, rollupAddress, storeLog);
const archiverStore = new KVArchiverDataStore(store, archiverConfig.maxLogs);

const telemetry = createAndStartTelemetryClient(getTelemetryClientConfig());
1 change: 1 addition & 0 deletions yarn-project/aztec/src/sandbox.ts
Original file line number Diff line number Diff line change
@@ -132,6 +132,7 @@ export async function deployContractsToL1(
l2FeeJuiceAddress: FeeJuiceAddress,
vkTreeRoot: getVKTreeRoot(),
assumeProvenUntil: opts.assumeProvenUntilBlockNumber,
salt: undefined,
}),
);

16 changes: 0 additions & 16 deletions yarn-project/aztec/terraform/node/main.tf
Original file line number Diff line number Diff line change
@@ -148,18 +148,6 @@ resource "aws_ecs_task_definition" "aztec-node" {
}

container_definitions = jsonencode([
{
name = "init-container"
image = "amazonlinux:latest"
essential = false
command = ["sh", "-c", "mkdir -p ${local.data_dir}/node_${count.index + 1}/data ${local.data_dir}/node_${count.index + 1}/temp"]
mountPoints = [
{
containerPath = local.data_dir
sourceVolume = "efs-data-store"
}
]
},
{
name = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}"
image = "${var.DOCKERHUB_ACCOUNT}/aztec:${var.IMAGE_TAG}"
@@ -376,10 +364,6 @@ resource "aws_ecs_task_definition" "aztec-node" {
}
]
dependsOn = [
{
containerName = "init-container"
condition = "COMPLETE"
}
]
logConfiguration = {
logDriver = "awslogs"
16 changes: 0 additions & 16 deletions yarn-project/aztec/terraform/prover-node/main.tf
Original file line number Diff line number Diff line change
@@ -148,18 +148,6 @@ resource "aws_ecs_task_definition" "aztec-prover-node" {
}

container_definitions = jsonencode([
{
name = "init-container"
image = "amazonlinux:latest"
essential = false
command = ["sh", "-c", "mkdir -p ${local.data_dir}/prover_node_${count.index + 1}/data ${local.data_dir}/prover_node_${count.index + 1}/temp"]
mountPoints = [
{
containerPath = local.data_dir
sourceVolume = "efs-data-store"
}
]
},
{
name = "${var.DEPLOY_TAG}-aztec-prover-node-${count.index + 1}"
image = "${var.DOCKERHUB_ACCOUNT}/aztec:${var.IMAGE_TAG}"
@@ -246,10 +234,6 @@ resource "aws_ecs_task_definition" "aztec-prover-node" {
}
]
dependsOn = [
{
containerName = "init-container"
condition = "COMPLETE"
}
]
logConfiguration = {
logDriver = "awslogs"
10 changes: 8 additions & 2 deletions yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ import {
} from '@aztec/circuits.js';
import { siloNoteHash } from '@aztec/circuits.js/hash';
import { runInDirectory } from '@aztec/foundation/fs';
import { createDebugLogger } from '@aztec/foundation/log';
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { Timer } from '@aztec/foundation/timer';
import {
ClientCircuitArtifacts,
@@ -57,6 +57,7 @@ import {
executeBbClientIvcProof,
verifyProof,
} from '../bb/execute.js';
import { type BBConfig } from '../config.js';
import { mapProtocolArtifactNameToCircuitName } from '../stats.js';
import { extractVkData } from '../verification_key/verification_key_data.js';

@@ -73,13 +74,18 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver {
Promise<VerificationKeyData>
>();

constructor(
private constructor(
private bbBinaryPath: string,
private bbWorkingDirectory: string,
private skipCleanup: boolean,
private log = createDebugLogger('aztec:bb-native-prover'),
) {}

public static async new(config: BBConfig, log?: DebugLogger) {
await fs.mkdir(config.bbWorkingDirectory, { recursive: true });
return new BBNativePrivateKernelProver(config.bbBinaryPath, config.bbWorkingDirectory, !!config.bbSkipCleanup, log);
}

private async _createClientIvcProof(
directory: string,
acirs: Buffer[],
1 change: 1 addition & 0 deletions yarn-project/bb-prover/src/verifier/bb_verifier.ts
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier {
initialCircuits: ProtocolArtifact[] = [],
logger = createDebugLogger('aztec:bb-verifier'),
) {
await fs.mkdir(config.bbWorkingDirectory, { recursive: true });
const keys = new Map<ProtocolArtifact, Promise<VerificationKeyData>>();
for (const circuit of initialCircuits) {
const vkData = await this.generateVerificationKey(
16 changes: 2 additions & 14 deletions yarn-project/cli/src/cmds/devnet/bootstrap_network.ts
Original file line number Diff line number Diff line change
@@ -112,20 +112,8 @@ async function deployERC20({ walletClient, publicClient }: L1Clients) {
contractBytecode: TokenPortalBytecode,
};

const erc20Address = await deployL1Contract(
walletClient,
publicClient,
erc20.contractAbi,
erc20.contractBytecode,
[],
);
const portalAddress = await deployL1Contract(
walletClient,
publicClient,
portal.contractAbi,
portal.contractBytecode,
[],
);
const erc20Address = await deployL1Contract(walletClient, publicClient, erc20.contractAbi, erc20.contractBytecode);
const portalAddress = await deployL1Contract(walletClient, publicClient, portal.contractAbi, portal.contractBytecode);

return {
erc20Address,
3 changes: 2 additions & 1 deletion yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts
Original file line number Diff line number Diff line change
@@ -7,11 +7,12 @@ export async function deployL1Contracts(
chainId: number,
privateKey: string | undefined,
mnemonic: string,
salt: number | undefined,
json: boolean,
log: LogFn,
debugLogger: DebugLogger,
) {
const { l1ContractAddresses } = await deployAztecContracts(rpcUrl, chainId, privateKey, mnemonic, debugLogger);
const { l1ContractAddresses } = await deployAztecContracts(rpcUrl, chainId, privateKey, mnemonic, salt, debugLogger);

if (json) {
log(
2 changes: 2 additions & 0 deletions yarn-project/cli/src/cmds/l1/index.ts
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL
'test test test test test test test test test test test junk',
)
.addOption(l1ChainIdOption)
.option('--salt <number>', 'The optional salt to use in deployment', arg => parseInt(arg))
.option('--json', 'Output the contract addresses in JSON format')
.action(async options => {
const { deployL1Contracts } = await import('./deploy_l1_contracts.js');
@@ -38,6 +39,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL
options.l1ChainId,
options.privateKey,
options.mnemonic,
options.salt,
options.json,
log,
debugLogger,
2 changes: 2 additions & 0 deletions yarn-project/cli/src/utils/aztec.ts
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ export async function deployAztecContracts(
chainId: number,
privateKey: string | undefined,
mnemonic: string,
salt: number | undefined,
debugLogger: DebugLogger,
): Promise<DeployL1Contracts> {
const {
@@ -105,6 +106,7 @@ export async function deployAztecContracts(
return await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger, l1Artifacts, {
l2FeeJuiceAddress: FeeJuiceAddress,
vkTreeRoot: getVKTreeRoot(),
salt,
});
}

1 change: 1 addition & 0 deletions yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@ export const setupL1Contracts = async (
const l1Data = await deployL1Contracts(l1RpcUrl, account, foundry, logger, l1Artifacts, {
l2FeeJuiceAddress: FeeJuiceAddress,
vkTreeRoot: getVKTreeRoot(),
salt: undefined,
});

return l1Data;
1 change: 1 addition & 0 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
@@ -140,6 +140,7 @@ export const setupL1Contracts = async (
const l1Data = await deployL1Contracts(l1RpcUrl, account, foundry, logger, l1Artifacts, {
l2FeeJuiceAddress: FeeJuiceAddress,
vkTreeRoot: getVKTreeRoot(),
salt: undefined,
});

return l1Data;
151 changes: 87 additions & 64 deletions yarn-project/ethereum/src/deploy_l1_contracts.ts
Original file line number Diff line number Diff line change
@@ -11,11 +11,16 @@ import {
type HttpTransport,
type PublicClient,
type WalletClient,
concatHex,
createPublicClient,
createWalletClient,
encodeDeployData,
getAddress,
getContract,
getContractAddress,
http,
numberToHex,
padHex,
} from 'viem';
import { type HDAccount, type PrivateKeyAccount, mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
import { foundry } from 'viem/chains';
@@ -141,7 +146,7 @@ export const deployL1Contracts = async (
chain: Chain,
logger: DebugLogger,
contractsToDeploy: L1ContractArtifactsForDeployment,
args: { l2FeeJuiceAddress: AztecAddress; vkTreeRoot: Fr; assumeProvenUntil?: number },
args: { l2FeeJuiceAddress: AztecAddress; vkTreeRoot: Fr; assumeProvenUntil?: number; salt: number | undefined },
): Promise<DeployL1Contracts> => {
// We are assuming that you are running this on a local anvil node which have 1s block times
// To align better with actual deployment, we update the block interval to 12s
@@ -162,55 +167,29 @@ export const deployL1Contracts = async (
}
logger.info(`Set block interval to ${interval}`);

logger.debug('Deploying contracts...');
logger.info(`Deploying contracts from ${account.address.toString()}...`);

const walletClient = createWalletClient({
account,
chain,
transport: http(rpcUrl),
});
const publicClient = createPublicClient({
chain,
transport: http(rpcUrl),
});
const walletClient = createWalletClient({ account, chain, transport: http(rpcUrl) });
const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });
const deployer = new L1Deployer(walletClient, publicClient, args.salt, logger);

const registryAddress = await deployL1Contract(
walletClient,
publicClient,
contractsToDeploy.registry.contractAbi,
contractsToDeploy.registry.contractBytecode,
);
const registryAddress = await deployer.deploy(contractsToDeploy.registry, [account.address.toString()]);
logger.info(`Deployed Registry at ${registryAddress}`);

const availabilityOracleAddress = await deployL1Contract(
walletClient,
publicClient,
contractsToDeploy.availabilityOracle.contractAbi,
contractsToDeploy.availabilityOracle.contractBytecode,
);
const availabilityOracleAddress = await deployer.deploy(contractsToDeploy.availabilityOracle);
logger.info(`Deployed AvailabilityOracle at ${availabilityOracleAddress}`);

const feeJuiceAddress = await deployL1Contract(
walletClient,
publicClient,
contractsToDeploy.feeJuice.contractAbi,
contractsToDeploy.feeJuice.contractBytecode,
);
const feeJuiceAddress = await deployer.deploy(contractsToDeploy.feeJuice);

logger.info(`Deployed Fee Juice at ${feeJuiceAddress}`);

const rollupAddress = await deployL1Contract(
walletClient,
publicClient,
contractsToDeploy.rollup.contractAbi,
contractsToDeploy.rollup.contractBytecode,
[
getAddress(registryAddress.toString()),
getAddress(availabilityOracleAddress.toString()),
getAddress(feeJuiceAddress.toString()),
args.vkTreeRoot.toString(),
],
);
const rollupAddress = await deployer.deploy(contractsToDeploy.rollup, [
getAddress(registryAddress.toString()),
getAddress(availabilityOracleAddress.toString()),
getAddress(feeJuiceAddress.toString()),
args.vkTreeRoot.toString(),
account.address.toString(),
]);
logger.info(`Deployed Rollup at ${rollupAddress}`);

// Set initial blocks as proven if requested
@@ -253,15 +232,15 @@ export const deployL1Contracts = async (
abi: contractsToDeploy.registry.contractAbi,
client: walletClient,
});
await registryContract.write.upgrade([getAddress(rollupAddress.toString())], { account });
if (!(await registryContract.read.isRollupRegistered([getAddress(rollupAddress.toString())]))) {
await registryContract.write.upgrade([getAddress(rollupAddress.toString())], { account });
logger.verbose(`Upgraded registry contract at ${registryAddress} to rollup ${rollupAddress}`);
} else {
logger.verbose(`Registry ${registryAddress} has already registered rollup ${rollupAddress}`);
}

// this contract remains uninitialized because at this point we don't know the address of the Fee Juice on L2
const feeJuicePortalAddress = await deployL1Contract(
walletClient,
publicClient,
contractsToDeploy.feeJuicePortal.contractAbi,
contractsToDeploy.feeJuicePortal.contractBytecode,
);
const feeJuicePortalAddress = await deployer.deploy(contractsToDeploy.feeJuicePortal);

logger.info(`Deployed Gas Portal at ${feeJuicePortalAddress}`);

@@ -310,6 +289,33 @@ export const deployL1Contracts = async (
};
};

class L1Deployer {
private salt: Hex | undefined;
constructor(
private walletClient: WalletClient<HttpTransport, Chain, Account>,
private publicClient: PublicClient<HttpTransport, Chain>,
maybeSalt: number | undefined,
private logger: DebugLogger,
) {
this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), { size: 32 }) : undefined;
}

deploy(
params: { contractAbi: Narrow<Abi | readonly unknown[]>; contractBytecode: Hex },
args: readonly unknown[] = [],
): Promise<EthAddress> {
return deployL1Contract(
this.walletClient,
this.publicClient,
params.contractAbi,
params.contractBytecode,
args,
this.salt,
this.logger,
);
}
}

// docs:start:deployL1Contract
/**
* Helper function to deploy ETH contracts.
@@ -326,23 +332,40 @@ export async function deployL1Contract(
abi: Narrow<Abi | readonly unknown[]>,
bytecode: Hex,
args: readonly unknown[] = [],
maybeSalt?: Hex,
logger?: DebugLogger,
): Promise<EthAddress> {
const hash = await walletClient.deployContract({
abi,
bytecode,
args,
});

const receipt = await publicClient.waitForTransactionReceipt({ hash, pollingInterval: 100 });
const contractAddress = receipt.contractAddress;
if (!contractAddress) {
throw new Error(
`No contract address found in receipt: ${JSON.stringify(receipt, (_, val) =>
typeof val === 'bigint' ? String(val) : val,
)}`,
);
if (maybeSalt) {
const salt = padHex(maybeSalt, { size: 32 });
const deployer: Hex = '0x4e59b44847b379578588920cA78FbF26c0B4956C';
const calldata = encodeDeployData({ abi, bytecode, args });
const address = getContractAddress({ from: deployer, salt, bytecode: calldata, opcode: 'CREATE2' });
const existing = await publicClient.getBytecode({ address });

if (existing === undefined || existing === '0x') {
const hash = await walletClient.sendTransaction({
to: deployer,
data: concatHex([salt, calldata]),
});
logger?.verbose(`Deploying contract with salt ${salt} to address ${address} in tx ${hash}`);
await publicClient.waitForTransactionReceipt({ hash, pollingInterval: 100 });
} else {
logger?.verbose(`Skipping existing deployment of contract with salt ${salt} to address ${address}`);
}
return EthAddress.fromString(address);
} else {
const hash = await walletClient.deployContract({ abi, bytecode, args });
logger?.verbose(`Deploying contract in tx ${hash}`);
const receipt = await publicClient.waitForTransactionReceipt({ hash, pollingInterval: 100 });
const contractAddress = receipt.contractAddress;
if (!contractAddress) {
throw new Error(
`No contract address found in receipt: ${JSON.stringify(receipt, (_, val) =>
typeof val === 'bigint' ? String(val) : val,
)}`,
);
}
return EthAddress.fromString(contractAddress);
}

return EthAddress.fromString(receipt.contractAddress!);
}
// docs:end:deployL1Contract
6 changes: 5 additions & 1 deletion yarn-project/kv-store/src/lmdb/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createDebugLogger } from '@aztec/foundation/log';

import { mkdirSync } from 'fs';
import { mkdtemp } from 'fs/promises';
import { type Database, type Key, type RootDatabase, open } from 'lmdb';
import { tmpdir } from 'os';
@@ -60,7 +61,10 @@ export class AztecLmdbStore implements AztecKVStore {
ephemeral: boolean = false,
log = createDebugLogger('aztec:kv-store:lmdb'),
): AztecLmdbStore {
log.verbose(`Opening LMDB database at ${path || 'temporary location'}`);
log.debug(`Opening LMDB database at ${path || 'temporary location'}`);
if (path) {
mkdirSync(path, { recursive: true });
}
const rootDb = open({ path, noSync: ephemeral });
return new AztecLmdbStore(rootDb, ephemeral, path);
}
19 changes: 8 additions & 11 deletions yarn-project/kv-store/src/utils.ts
Original file line number Diff line number Diff line change
@@ -5,17 +5,13 @@ import { type AztecKVStore } from './interfaces/store.js';
import { AztecLmdbStore } from './lmdb/store.js';

export function createStore(
config: { dataDirectory: string | undefined },
config: { dataDirectory: string | undefined } | (string | undefined),
rollupAddress: EthAddress,
log: Logger = createDebugLogger('aztec:kv-store'),
) {
if (config.dataDirectory) {
log.info(`Using data directory: ${config.dataDirectory}`);
} else {
log.info('Using ephemeral data directory');
}

return initStoreForRollup(AztecLmdbStore.open(config.dataDirectory, false), rollupAddress, log);
const dataDirectory = typeof config === 'string' ? config : config?.dataDirectory;
log.info(dataDirectory ? `Creating data store at directory ${dataDirectory}` : 'Creating ephemeral data store');
return initStoreForRollup(AztecLmdbStore.open(dataDirectory, false), rollupAddress, log);
}

/**
@@ -38,9 +34,10 @@ export async function initStoreForRollup<T extends AztecKVStore>(
const storedRollupAddressString = rollupAddressValue.get();

if (typeof storedRollupAddressString !== 'undefined' && storedRollupAddressString !== rollupAddressString) {
log?.warn(
`Rollup address mismatch: expected ${rollupAddress}, found ${rollupAddressValue}. Clearing entire database...`,
);
log?.warn(`Rollup address mismatch. Clearing entire database...`, {
expected: rollupAddressString,
found: storedRollupAddressString,
});

await store.clear();
}
1 change: 1 addition & 0 deletions yarn-project/prover-node/src/prover-node.ts
Original file line number Diff line number Diff line change
@@ -124,6 +124,7 @@ export class ProverNode {
}

// Fast forward world state to right before the target block and get a fork
this.log.verbose(`Creating proving job for block ${fromBlock}`);
const db = await this.worldState.syncImmediateAndFork(fromBlock - 1, true);

// Create a processor using the forked world state
38 changes: 18 additions & 20 deletions yarn-project/pxe/src/pxe_service/create_pxe_service.ts
Original file line number Diff line number Diff line change
@@ -3,8 +3,7 @@ import { type AztecNode, type PrivateKernelProver } from '@aztec/circuit-types';
import { randomBytes } from '@aztec/foundation/crypto';
import { createDebugLogger } from '@aztec/foundation/log';
import { KeyStore } from '@aztec/key-store';
import { AztecLmdbStore } from '@aztec/kv-store/lmdb';
import { initStoreForRollup } from '@aztec/kv-store/utils';
import { createStore } from '@aztec/kv-store/utils';
import { getCanonicalAuthRegistry } from '@aztec/protocol-contracts/auth-registry';
import { getCanonicalClassRegisterer } from '@aztec/protocol-contracts/class-registerer';
import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice';
@@ -43,25 +42,10 @@ export async function createPXEService(
const keyStorePath = config.dataDirectory ? join(config.dataDirectory, 'pxe_key_store') : undefined;
const l1Contracts = await aztecNode.getL1ContractAddresses();

const keyStore = new KeyStore(await initStoreForRollup(AztecLmdbStore.open(keyStorePath), l1Contracts.rollupAddress));
const db = new KVPxeDatabase(await initStoreForRollup(AztecLmdbStore.open(pxeDbPath), l1Contracts.rollupAddress));

// (@PhilWindle) Temporary validation until WASM is implemented
let prover: PrivateKernelProver | undefined = proofCreator;
if (!prover) {
if (config.proverEnabled && (!config.bbBinaryPath || !config.bbWorkingDirectory)) {
throw new Error(`Prover must be configured with binary path and working directory`);
}
prover = !config.proverEnabled
? new TestPrivateKernelProver()
: new BBNativePrivateKernelProver(
config.bbBinaryPath!,
config.bbWorkingDirectory!,
!!config.bbSkipCleanup,
createDebugLogger('aztec:pxe:bb-native-prover' + (logSuffix ? `:${logSuffix}` : '')),
);
}
const keyStore = new KeyStore(await createStore(keyStorePath, l1Contracts.rollupAddress));
const db = new KVPxeDatabase(await createStore(pxeDbPath, l1Contracts.rollupAddress));

const prover = proofCreator ?? (await createProver(config, logSuffix));
const server = new PXEService(keyStore, aztecNode, db, prover, config, logSuffix);
for (const contract of [
getCanonicalClassRegisterer(),
@@ -77,3 +61,17 @@ export async function createPXEService(
await server.start();
return server;
}

function createProver(config: PXEServiceConfig, logSuffix?: string) {
if (!config.proverEnabled) {
return new TestPrivateKernelProver();
}

// (@PhilWindle) Temporary validation until WASM is implemented
if (!config.bbBinaryPath || !config.bbWorkingDirectory) {
throw new Error(`Prover must be configured with binary path and working directory`);
}
const bbConfig = config as Required<Pick<PXEServiceConfig, 'bbBinaryPath' | 'bbWorkingDirectory'>> & PXEServiceConfig;
const log = createDebugLogger('aztec:pxe:bb-native-prover' + (logSuffix ? `:${logSuffix}` : ''));
return BBNativePrivateKernelProver.new(bbConfig, log);
}