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
5 changes: 5 additions & 0 deletions l1-contracts/script/deploy/DeployRollupForUpgrade.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,14 @@ contract DeployRollupForUpgrade is Script {
console.log("JSON DEPLOY RESULT:", finalJson);
}

function _requireRegistryHasCode(Registry _registry) internal view {
require(address(_registry).code.length > 0, "DeployRollupForUpgrade: REGISTRY_ADDRESS has no code on this chain");
}

/// @notice Parse existing L1 infrastructure from environment variables
function _getRollupAddressInput() internal returns (RollupAddressInput memory) {
Registry registry = Registry(vm.envAddress("REGISTRY_ADDRESS"));
_requireRegistryHasCode(registry);

// Load existing addresses from the registry and canonical rollup.
Governance governance = Governance(registry.getGovernance());
Expand Down
35 changes: 35 additions & 0 deletions l1-contracts/script/deploy/DeployRollupLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,47 @@ library DeployRollupLib {
GenesisState memory genesisState = config.getGenesisState();
RollupConfigInput memory rollupConfigInput =
config.getRollupConfiguration(IRewardDistributor(address(input.rewardDistributor)));
validateRollupConfig(input, rollupConfigInput);

return new Rollup(
input.feeAsset, input.stakingAsset, input.gse, verifier, input.deployer, genesisState, rollupConfigInput
);
}

/// @notice Reject configuration the Rollup constructor accepts but that leaves the deployed instance unusable.
/// @dev Every value here is fixed at construction, so a bad one can only be corrected by redeploying. A zero
/// `targetCommitteeSize` is not rejected: `ValidatorSelectionLib` treats it as "no committee", which local
/// networks and end-to-end tests rely on. An epoch longer than `MAX_CHECKPOINTS_PER_EPOCH` is not rejected
/// either: end-to-end tests use long epochs to keep a run inside epoch 0.
function validateRollupConfig(RollupAddressInput memory input, RollupConfigInput memory config) internal view {
require(config.ethereumSlotDuration > 0, "DeployRollupLib: ethereumSlotDuration is zero");
require(config.aztecSlotDuration > 0, "DeployRollupLib: aztecSlotDuration is zero");
require(config.aztecEpochDuration > 0, "DeployRollupLib: aztecEpochDuration is zero");
// TimeLib multiplies the two uint32 durations without widening.
uint256 epochDurationSeconds = config.aztecSlotDuration * config.aztecEpochDuration;
require(epochDurationSeconds <= type(uint32).max, "DeployRollupLib: epoch duration in seconds overflows uint32");
require(config.exitDelaySeconds > 0, "DeployRollupLib: exitDelaySeconds is zero");
// A zero RANDAO lag samples the same epoch it seeds, and the sample-time subtraction is done in uint32.
require(config.lagInEpochsForRandao >= 1, "DeployRollupLib: lagInEpochsForRandao is zero");
require(
config.lagInEpochsForValidatorSet >= config.lagInEpochsForRandao,
"DeployRollupLib: lagInEpochsForValidatorSet below lagInEpochsForRandao"
);
require(
config.lagInEpochsForValidatorSet * epochDurationSeconds <= type(uint32).max,
"DeployRollupLib: lag in seconds overflows uint32"
);
// SlashingProposer encodes each amount as uint96 when it builds a payload.
for (uint256 i = 0; i < config.slashAmounts.length; i++) {
require(config.slashAmounts[i] <= type(uint96).max, "DeployRollupLib: slash amount exceeds uint96");
}
require(address(config.rewardConfig.rewardDistributor) != address(0), "DeployRollupLib: rewardDistributor is zero");
// The GSE only accounts stake in its own asset.
require(
address(input.stakingAsset) == address(input.gse.ASSET()), "DeployRollupLib: stakingAsset is not GSE.ASSET()"
);
}

function _maybeMintInitialFeeAsset(RollupAddressInput memory input, Rollup rollup, IRollupConfiguration config)
private
{
Expand Down
25 changes: 23 additions & 2 deletions l1-contracts/script/deploy/RollupConfiguration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,43 @@ contract RollupConfiguration is IRollupConfiguration, Test {
function loadConfig() external {
networkName = vm.envOr("NETWORK", string("local"));
validatorsJson = vm.envOr("INITIAL_VALIDATORS", string("[]"));

_requireNetworkMatchesChain(networkName, block.chainid);
}

/// @dev NETWORK selects the contract defaults and the chain id selects RPC and keys; a mismatch means one of
/// them points at the wrong network.
function _requireNetworkMatchesChain(string memory _networkName, uint256 _chainId) internal pure {
bool isMainnetName = keccak256(bytes(_networkName)) == keccak256("mainnet");
require(!isMainnetName || _chainId == 1, "RollupConfiguration: NETWORK=mainnet requires chain id 1");
require(_chainId != 1 || isMainnetName, "RollupConfiguration: chain id 1 requires NETWORK=mainnet");
}

/// @dev A stub verifier accepts any proof, so it must be requested explicitly with REAL_VERIFIER=false.
function useRealVerifier() external view returns (bool) {
return vm.envOr("REAL_VERIFIER", false);
return vm.envOr("REAL_VERIFIER", true);
}

function getFeeJuicePortalInitialBalance() external view returns (uint256) {
return vm.envOr("FEE_JUICE_PORTAL_INITIAL_BALANCE", uint256(0));
}

/// @dev The genesis roots are immutable once the rollup is constructed and a zero root cannot match any node's
/// view of the chain, so each one must be supplied explicitly.
function getGenesisState() external view returns (GenesisState memory) {
return GenesisState({
GenesisState memory genesisState = GenesisState({
vkTreeRoot: bytes32(vm.envOr("VK_TREE_ROOT", uint256(0))),
protocolContractsHash: bytes32(vm.envOr("PROTOCOL_CONTRACTS_HASH", uint256(0))),
genesisArchiveRoot: bytes32(vm.envOr("GENESIS_ARCHIVE_ROOT", uint256(0)))
});
_requireGenesisState(genesisState);
return genesisState;
}

function _requireGenesisState(GenesisState memory _genesisState) internal pure {
require(_genesisState.vkTreeRoot != bytes32(0), "RollupConfiguration: VK_TREE_ROOT is unset");
require(_genesisState.protocolContractsHash != bytes32(0), "RollupConfiguration: PROTOCOL_CONTRACTS_HASH is unset");
require(_genesisState.genesisArchiveRoot != bytes32(0), "RollupConfiguration: GENESIS_ARCHIVE_ROOT is unset");
}

function getRewardConfiguration(IRewardDistributor _rewardDistributor) external pure returns (RewardConfig memory) {
Expand Down
1 change: 0 additions & 1 deletion l1-contracts/scripts/run_rollup_upgrade.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ echo "=== Deploying rollup upgrade ==="
echo "Registry: $registry_address"

REGISTRY_ADDRESS="$registry_address" \
REAL_VERIFIER="${REAL_VERIFIER:-true}" \
./scripts/forge_broadcast.js \
script/deploy/DeployRollupForUpgrade.s.sol:DeployRollupForUpgrade \
--rpc-url "$L1_RPC_URL" \
Expand Down
6 changes: 6 additions & 0 deletions l1-contracts/scripts/stress_test_deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ echo "=== Results dir: $RESULTS_DIR ==="

source ./scripts/load_network_defaults.sh

# The deployer requires explicit genesis roots. Only 31 random bytes (top byte zero) so each value is always
# below the BN254 scalar field modulus.
export VK_TREE_ROOT="0x00$(openssl rand -hex 31)"
export PROTOCOL_CONTRACTS_HASH="0x00$(openssl rand -hex 31)"
export GENESIS_ARCHIVE_ROOT="0x00$(openssl rand -hex 31)"

PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

# Pre-compile so workers skip compilation.
Expand Down
9 changes: 7 additions & 2 deletions l1-contracts/scripts/test_rollup_upgrade.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ trap cleanup EXIT
# Clean stale broadcast artifacts from previous runs to avoid nonce conflicts.
rm -rf broadcast/

# The deployer requires explicit genesis roots. Only 31 random bytes (top byte zero) so each value is always
# below the BN254 scalar field modulus.
export VK_TREE_ROOT="0x00$(openssl rand -hex 31)"
export PROTOCOL_CONTRACTS_HASH="0x00$(openssl rand -hex 31)"
export GENESIS_ARCHIVE_ROOT="0x00$(openssl rand -hex 31)"

# Fixed port — this test runs with ISOLATE=1 so no conflicts.
ANVIL_PORT="${ANVIL_PORT:-8545}"

Expand Down Expand Up @@ -51,8 +57,7 @@ if [[ -z "$registry_address" || "$registry_address" == "null" ]]; then
fi

echo "=== Testing run_rollup_upgrade.sh ==="
# Use a different genesis to get a different rollup version. Only 31 random bytes (top byte zero) so the value is
# always below the BN254 scalar field modulus; the rollup rejects a genesis archive root >= the field modulus.
# Use a different genesis to get a different rollup version.
export GENESIS_ARCHIVE_ROOT="0x00$(openssl rand -hex 31)"

./scripts/run_rollup_upgrade.sh "$registry_address"
Expand Down
5 changes: 5 additions & 0 deletions l1-contracts/test/script/DeployAztecL1Contracts.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ contract DeployAztecL1ContractsTest is Test {
string memory path = string.concat(root, "/scripts/network-defaults.json");
string memory json = vm.readFile(path);

// Genesis roots are required by the deployer; any non-zero field element will do here.
vm.setEnv("VK_TREE_ROOT", vm.toString(uint256(keccak256("vk_tree_root")) >> 8));
vm.setEnv("PROTOCOL_CONTRACTS_HASH", vm.toString(uint256(keccak256("protocol_contracts_hash")) >> 8));
vm.setEnv("GENESIS_ARCHIVE_ROOT", vm.toString(uint256(keccak256("genesis_archive_root")) >> 8));

// Timing config
vm.setEnv("ETHEREUM_SLOT_DURATION", vm.toString(json.readUint(".ETHEREUM_SLOT_DURATION")));
vm.setEnv("AZTEC_SLOT_DURATION", vm.toString(json.readUint(".AZTEC_SLOT_DURATION")));
Expand Down
Loading
Loading