Skip to content
Closed
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
19 changes: 0 additions & 19 deletions l1-contracts/script/deploy/RollupConfiguration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ contract RollupConfiguration is IRollupConfiguration, Test {
config.rewardBoostConfig = this.getRewardBoostConfiguration();
config.stakingQueueConfig = this.getStakingQueueConfiguration();

// Compute version as first 4 bytes of hash(abi.encode(config, genesisState))
config.version = _computeConfigVersion(config, this.getGenesisState());

return config;
}

Expand All @@ -156,7 +153,6 @@ contract RollupConfiguration is IRollupConfiguration, Test {
config.slashingDisableDuration = vm.envUint("AZTEC_SLASHING_DISABLE_DURATION");
config.manaTarget = vm.envUint("AZTEC_MANA_TARGET");
config.exitDelaySeconds = vm.envUint("AZTEC_EXIT_DELAY_SECONDS");
config.version = 0; // Computed below
config.provingCostPerMana = EthValue.wrap(vm.envUint("AZTEC_PROVING_COST_PER_MANA"));
config.initialEthPerFeeAsset = EthPerFeeAssetE12.wrap(vm.envUint("AZTEC_INITIAL_ETH_PER_FEE_ASSET"));
config.registryRewardOverrides[0] = _getRegistryRewardOverride("AZTEC_REGISTRY_REWARD_OVERRIDE_0");
Expand All @@ -179,21 +175,6 @@ contract RollupConfiguration is IRollupConfiguration, Test {
registryRewardOverride.sequencerReward = vm.parseUint(fields[1]).toUint96();
}

/// @notice Compute rollup config version by hashing config + genesis state
/// @dev Version is the first 4 bytes (uint32) of keccak256(abi.encode(rollupConfig, genesisState))
/// This DOES NOT match the TS implementation: keccak256(jsonStringify({rollupConfigArgs, genesisStateArgs}))
function _computeConfigVersion(RollupConfigInput memory _config, GenesisState memory _genesisState)
private
pure
returns (uint32)
{
bytes32 hash = keccak256(abi.encode(_config, _genesisState));
// Extract first 4 bytes as uint32 (big-endian)
// Casting to bytes4 is intentional because the config version is defined as the first 4 bytes of the hash.
// forge-lint: disable-next-line(unsafe-typecast)
return uint32(bytes4(hash));
}

function _getSlashingOffset() private view returns (uint256) {
return vm.envUint("AZTEC_SLASHING_OFFSET_IN_ROUNDS");
}
Expand Down
12 changes: 9 additions & 3 deletions l1-contracts/src/core/RollupCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
GenesisState memory _genesisState,
RollupConfigInput memory _config
) Ownable(_governance) {
// Hashed before anything below can modify `_config` in memory. Including this contract's address means the
// deployer cannot choose the version and two deployments do not share one. The 32-bit truncation is still
// reachable by grinding CREATE2 salts, so it separates deployments rather than identifying them.
// Casting to bytes4 is intentional because the version is defined as the first 4 bytes of the hash.
// forge-lint: disable-next-line(unsafe-typecast)
VERSION = uint32(bytes4(keccak256(abi.encode(block.chainid, address(this), _config, _genesisState))));

StakingLib.assertValidQueueConfig(_config.stakingQueueConfig);

// queueSetSlasher schedules the replacement slasher to land at `block.timestamp +
Expand Down Expand Up @@ -294,13 +301,12 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
// factored out into a helper the way the slasher and reward setup are.
VK_TREE_ROOT = _genesisState.vkTreeRoot;
PROTOCOL_CONTRACTS_HASH = _genesisState.protocolContractsHash;
VERSION = _config.version;
FEE_ASSET = _feeAsset;
EPOCH_PROOF_VERIFIER = _epochProofVerifier;

IInbox inbox = IInbox(address(new Inbox(address(this), _feeAsset, _config.version, INBOX_BUCKET_RING_SIZE)));
IInbox inbox = IInbox(address(new Inbox(address(this), _feeAsset, VERSION, INBOX_BUCKET_RING_SIZE)));
INBOX = inbox;
OUTBOX = IOutbox(address(new Outbox(address(this), _config.version)));
OUTBOX = IOutbox(address(new Outbox(address(this), VERSION)));
FEE_ASSET_PORTAL = IFeeJuicePortal(inbox.getFeeAssetPortal());

STFLib.initialize(_genesisState);
Expand Down
1 change: 0 additions & 1 deletion l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ struct RollupConfigInput {
uint256 slashingDisableDuration;
uint256 manaTarget;
uint256 exitDelaySeconds;
uint32 version;
EthValue provingCostPerMana;
EthPerFeeAssetE12 initialEthPerFeeAsset;
RewardConfig rewardConfig;
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/src/governance/Registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {RewardDistributor, IRewardDistributor} from "./RewardDistributor.sol";
struct RegistryStorage {
/**
* @notice Mapping from version to rollup instance
* @dev As implemented today, the version is a truncated hash of identifiers of the rollup instance
* See RollupCore.sol for the implementation.
* @dev The version is a truncated hash of the chain id, the rollup's address, its configuration and its
* genesis state, computed by the rollup itself at construction. See RollupCore.sol for the implementation.
* @dev updated when a new rollup instance is added, which becomes the new canonical rollup
*/
mapping(uint256 version => IHaveVersion rollup) versionToRollup;
Expand Down
52 changes: 52 additions & 0 deletions l1-contracts/test/RollupVersion.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Aztec Labs.
// solhint-disable func-name-mixedcase
pragma solidity >=0.8.27;

import {Test} from "forge-std/Test.sol";
import {RollupBuilder} from "./builder/RollupBuilder.sol";
import {Rollup} from "@aztec/core/Rollup.sol";
import {Inbox} from "@aztec/core/messagebridge/Inbox.sol";
import {Outbox} from "@aztec/core/messagebridge/Outbox.sol";
import {Registry} from "@aztec/governance/Registry.sol";
import {IHaveVersion} from "@aztec/governance/interfaces/IRegistry.sol";

contract RollupVersionTest is Test {
function test_versionIsDerivedFromChainIdAddressConfigAndGenesis() external {
RollupBuilder builder = new RollupBuilder(address(this)).deploy();
Rollup rollup = Rollup(address(builder.getConfig().rollup));

uint256 expected = uint32(
bytes4(
keccak256(
abi.encode(
block.chainid, address(rollup), builder.getConfig().rollupConfigInput, builder.getConfig().genesisState
)
)
)
);
assertEq(rollup.getVersion(), expected, "version");
assertEq(Inbox(address(rollup.getInbox())).VERSION(), expected, "inbox version");
assertEq(Outbox(address(rollup.getOutbox())).VERSION(), expected, "outbox version");
}

function test_identicallyConfiguredRollupsHaveDistinctVersions() external {
RollupBuilder first = new RollupBuilder(address(this)).deploy();
Registry registry = first.getConfig().registry;
Rollup firstRollup = Rollup(address(first.getConfig().rollup));

// Same registry, GSE, assets, genesis state and rollup config as the first rollup.
RollupBuilder second = new RollupBuilder(address(this)).setGSE(first.getConfig().gse)
.setTestERC20(first.getConfig().testERC20).setRegistry(registry).setMakeCanonical(false).setMakeGovernance(false)
.setUpdateOwnerships(false).deploy();
Rollup secondRollup = Rollup(address(second.getConfig().rollup));

assertNotEq(firstRollup.getVersion(), secondRollup.getVersion(), "versions collide");

// Both can be registered side by side, since the Registry is keyed by version.
vm.prank(registry.owner());
registry.addRollup(IHaveVersion(address(secondRollup)));
assertEq(address(registry.getRollup(firstRollup.getVersion())), address(firstRollup), "first rollup");
assertEq(address(registry.getRollup(secondRollup.getVersion())), address(secondRollup), "second rollup");
}
}
1 change: 0 additions & 1 deletion l1-contracts/test/builder/RollupBuilder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ contract RollupBuilder is Test {
target.slashingDisableDuration = _source.slashingDisableDuration;
target.manaTarget = _source.manaTarget;
target.exitDelaySeconds = _source.exitDelaySeconds;
target.version = _source.version;
target.provingCostPerMana = _source.provingCostPerMana;
target.initialEthPerFeeAsset = _source.initialEthPerFeeAsset;
target.rewardConfig = _source.rewardConfig;
Expand Down
7 changes: 0 additions & 7 deletions l1-contracts/test/harnesses/TestConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ library TestConstants {
config.exitDelaySeconds = AZTEC_EXIT_DELAY_SECONDS;
config.provingCostPerMana = AZTEC_PROVING_COST_PER_MANA;
config.initialEthPerFeeAsset = AZTEC_INITIAL_ETH_PER_FEE_ASSET;
config.version = 0;
config.rewardConfig = rewardConfig;
config.rewardBoostConfig = rewardBoostConfig;
config.stakingQueueConfig = stakingQueueConfig;
Expand All @@ -134,12 +133,6 @@ library TestConstants {
config.localEjectionThreshold = 0;
config.ethereumSlotDuration = ETHEREUM_SLOT_DURATION;

// For the version we derive it based on the config (with a 0 version)
// TODO(https://linear.app/aztec-labs/issue/TMNT-139/version-at-deployment)
uint32 version =
uint32(uint256(keccak256(abi.encode(bytes("aztec_rollup"), block.chainid, getGenesisState(), config))));
config.version = version;

return config;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ contract InitiateWithdrawByAttesterTest is StakingBase {
RollupBuilder builder = new RollupBuilder(address(this)).setGSE(gse).setTestERC20(stakingAsset)
.setRegistry(registry).setMakeCanonical(false).setMakeGovernance(false).setUpdateOwnerships(false);
RollupConfigInput memory config = builder.getConfig().rollupConfigInput;
config.version = uint32(IHaveVersion(address(staking)).getVersion() + 1);
builder.setRollupConfigInput(config).deploy();
nextRollup = IStaking(address(builder.getConfig().rollup));

Expand Down
Loading