Skip to content

Commit

Permalink
Feat: Refactor pause manager structure (#126)
Browse files Browse the repository at this point in the history
* feat: add PauseType enum

* fix: refactor test utils structure

* fix: add events to PauseManager for pauseType setting

* fix: update linea rollup deployment script

* fix: change utils folder to common

* fix: refactor roles constant and deployment scripts

* fix: add additional check in PermissionManager to prevent empty role assignments

* fix: update initialize functions with an additional param

* fix: update deployment scripts

* fix: update contracts testing

* fix: linting issue

* rename PAUSE_L2_BLOB_SUBMISSION_ROLE to PAUSE_BLOB_SUBMISSION_ROLE
  • Loading branch information
VGau authored Oct 4, 2024
1 parent 2db07df commit efbec20
Show file tree
Hide file tree
Showing 108 changed files with 1,820 additions and 1,358 deletions.
9 changes: 9 additions & 0 deletions contracts/common/constants/general.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ZeroHash } from "ethers";

export const HASH_ZERO = ZeroHash;

export const LINEA_ROLLUP_INITIALIZE_SIGNATURE =
"initialize((bytes32,uint256,uint256,address,uint256,uint256,(address,bytes32)[],(uint8,bytes32)[],(uint8,bytes32)[],address,address))";

export const L2_MESSAGE_SERVICE_INITIALIZE_SIGNATURE =
"initialize(uint256,uint256,address,(address,bytes32)[],(uint8,bytes32)[],(uint8,bytes32)[])";
3 changes: 3 additions & 0 deletions contracts/common/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./roles";
export * from "./general";
export * from "./pauseTypes";
73 changes: 73 additions & 0 deletions contracts/common/constants/pauseTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
PAUSE_ALL_ROLE,
PAUSE_COMPLETE_TOKEN_BRIDGING_ROLE,
PAUSE_FINALIZE_WITHPROOF_ROLE,
PAUSE_INITIATE_TOKEN_BRIDGING_ROLE,
PAUSE_L1_L2_ROLE,
PAUSE_BLOB_SUBMISSION_ROLE,
PAUSE_L2_L1_ROLE,
UNPAUSE_ALL_ROLE,
UNPAUSE_COMPLETE_TOKEN_BRIDGING_ROLE,
UNPAUSE_FINALIZE_WITHPROOF_ROLE,
UNPAUSE_INITIATE_TOKEN_BRIDGING_ROLE,
UNPAUSE_L1_L2_ROLE,
UNPAUSE_BLOB_SUBMISSION_ROLE,
UNPAUSE_L2_L1_ROLE,
} from "./roles";

export const GENERAL_PAUSE_TYPE = 1;
export const L1_L2_PAUSE_TYPE = 2;
export const L2_L1_PAUSE_TYPE = 3;
export const BLOB_SUBMISSION_PAUSE_TYPE = 4;
export const CALLDATA_SUBMISSION_PAUSE_TYPE = 5;
export const FINALIZATION_PAUSE_TYPE = 6;
export const INITIATE_TOKEN_BRIDGING_PAUSE_TYPE = 7;
export const COMPLETE_TOKEN_BRIDGING_PAUSE_TYPE = 8;

export const BASE_PAUSE_TYPES_ROLES = [{ pauseType: GENERAL_PAUSE_TYPE, role: PAUSE_ALL_ROLE }];
export const BASE_UNPAUSE_TYPES_ROLES = [{ pauseType: GENERAL_PAUSE_TYPE, role: UNPAUSE_ALL_ROLE }];

// LineaRollup
export const LINEA_ROLLUP_PAUSE_TYPES_ROLES = [
...BASE_PAUSE_TYPES_ROLES,
{ pauseType: L1_L2_PAUSE_TYPE, role: PAUSE_L1_L2_ROLE },
{ pauseType: L2_L1_PAUSE_TYPE, role: PAUSE_L2_L1_ROLE },
{ pauseType: BLOB_SUBMISSION_PAUSE_TYPE, role: PAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: CALLDATA_SUBMISSION_PAUSE_TYPE, role: PAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: FINALIZATION_PAUSE_TYPE, role: PAUSE_FINALIZE_WITHPROOF_ROLE },
];

export const LINEA_ROLLUP_UNPAUSE_TYPES_ROLES = [
...BASE_UNPAUSE_TYPES_ROLES,
{ pauseType: L1_L2_PAUSE_TYPE, role: UNPAUSE_L1_L2_ROLE },
{ pauseType: L2_L1_PAUSE_TYPE, role: UNPAUSE_L2_L1_ROLE },
{ pauseType: BLOB_SUBMISSION_PAUSE_TYPE, role: UNPAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: CALLDATA_SUBMISSION_PAUSE_TYPE, role: UNPAUSE_BLOB_SUBMISSION_ROLE },
{ pauseType: FINALIZATION_PAUSE_TYPE, role: UNPAUSE_FINALIZE_WITHPROOF_ROLE },
];

// L2MessageService
export const L2_MESSAGE_SERVICE_PAUSE_TYPES_ROLES = [
...BASE_PAUSE_TYPES_ROLES,
{ pauseType: L1_L2_PAUSE_TYPE, role: PAUSE_L1_L2_ROLE },
{ pauseType: L2_L1_PAUSE_TYPE, role: PAUSE_L2_L1_ROLE },
];

export const L2_MESSAGE_SERVICE_UNPAUSE_TYPES_ROLES = [
...BASE_UNPAUSE_TYPES_ROLES,
{ pauseType: L1_L2_PAUSE_TYPE, role: UNPAUSE_L1_L2_ROLE },
{ pauseType: L2_L1_PAUSE_TYPE, role: UNPAUSE_L2_L1_ROLE },
];

// TokenBridge
export const TOKEN_BRIDGE_PAUSE_TYPES_ROLES = [
...BASE_PAUSE_TYPES_ROLES,
{ pauseType: INITIATE_TOKEN_BRIDGING_PAUSE_TYPE, role: PAUSE_INITIATE_TOKEN_BRIDGING_ROLE },
{ pauseType: COMPLETE_TOKEN_BRIDGING_PAUSE_TYPE, role: PAUSE_COMPLETE_TOKEN_BRIDGING_ROLE },
];

export const TOKEN_BRIDGE_UNPAUSE_TYPES_ROLES = [
...BASE_UNPAUSE_TYPES_ROLES,
{ pauseType: INITIATE_TOKEN_BRIDGING_PAUSE_TYPE, role: UNPAUSE_INITIATE_TOKEN_BRIDGING_ROLE },
{ pauseType: COMPLETE_TOKEN_BRIDGING_PAUSE_TYPE, role: UNPAUSE_COMPLETE_TOKEN_BRIDGING_ROLE },
];
121 changes: 121 additions & 0 deletions contracts/common/constants/roles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { HASH_ZERO } from "./general";
import { generateKeccak256 } from "../helpers";

// Roles hashes
export const DEFAULT_ADMIN_ROLE = HASH_ZERO;
export const FUNCTION_EXECUTOR_ROLE = generateKeccak256(["string"], ["FUNCTION_EXECUTOR_ROLE"], { encodePacked: true });
export const RATE_LIMIT_SETTER_ROLE = generateKeccak256(["string"], ["RATE_LIMIT_SETTER_ROLE"], { encodePacked: true });
export const USED_RATE_LIMIT_RESETTER_ROLE = generateKeccak256(["string"], ["USED_RATE_LIMIT_RESETTER_ROLE"], {
encodePacked: true,
});
export const L1_L2_MESSAGE_SETTER_ROLE = generateKeccak256(["string"], ["L1_L2_MESSAGE_SETTER_ROLE"], {
encodePacked: true,
});
export const PAUSE_ALL_ROLE = generateKeccak256(["string"], ["PAUSE_ALL_ROLE"], { encodePacked: true });
export const UNPAUSE_ALL_ROLE = generateKeccak256(["string"], ["UNPAUSE_ALL_ROLE"], { encodePacked: true });
export const PAUSE_L1_L2_ROLE = generateKeccak256(["string"], ["PAUSE_L1_L2_ROLE"], { encodePacked: true });
export const UNPAUSE_L1_L2_ROLE = generateKeccak256(["string"], ["UNPAUSE_L1_L2_ROLE"], { encodePacked: true });
export const PAUSE_L2_L1_ROLE = generateKeccak256(["string"], ["PAUSE_L2_L1_ROLE"], { encodePacked: true });
export const UNPAUSE_L2_L1_ROLE = generateKeccak256(["string"], ["UNPAUSE_L2_L1_ROLE"], { encodePacked: true });
export const PAUSE_BLOB_SUBMISSION_ROLE = generateKeccak256(["string"], ["PAUSE_BLOB_SUBMISSION_ROLE"], {
encodePacked: true,
});
export const UNPAUSE_BLOB_SUBMISSION_ROLE = generateKeccak256(["string"], ["UNPAUSE_BLOB_SUBMISSION_ROLE"], {
encodePacked: true,
});
export const PAUSE_FINALIZE_WITHPROOF_ROLE = generateKeccak256(["string"], ["PAUSE_FINALIZE_WITHPROOF_ROLE"], {
encodePacked: true,
});
export const UNPAUSE_FINALIZE_WITHPROOF_ROLE = generateKeccak256(["string"], ["UNPAUSE_FINALIZE_WITHPROOF_ROLE"], {
encodePacked: true,
});
export const MINIMUM_FEE_SETTER_ROLE = generateKeccak256(["string"], ["MINIMUM_FEE_SETTER_ROLE"], {
encodePacked: true,
});
export const OPERATOR_ROLE = generateKeccak256(["string"], ["OPERATOR_ROLE"], { encodePacked: true });
export const VERIFIER_SETTER_ROLE = generateKeccak256(["string"], ["VERIFIER_SETTER_ROLE"], { encodePacked: true });
export const VERIFIER_UNSETTER_ROLE = generateKeccak256(["string"], ["VERIFIER_UNSETTER_ROLE"], { encodePacked: true });
export const L1_MERKLE_ROOTS_SETTER_ROLE = generateKeccak256(["string"], ["L1_MERKLE_ROOTS_SETTER_ROLE"], {
encodePacked: true,
});
export const L2_MERKLE_ROOTS_SETTER_ROLE = generateKeccak256(["string"], ["L2_MERKLE_ROOTS_SETTER_ROLE"], {
encodePacked: true,
});
export const PAUSE_INITIATE_TOKEN_BRIDGING_ROLE = generateKeccak256(
["string"],
["PAUSE_INITIATE_TOKEN_BRIDGING_ROLE"],
{ encodePacked: true },
);
export const PAUSE_COMPLETE_TOKEN_BRIDGING_ROLE = generateKeccak256(
["string"],
["PAUSE_COMPLETE_TOKEN_BRIDGING_ROLE"],
{ encodePacked: true },
);
export const UNPAUSE_INITIATE_TOKEN_BRIDGING_ROLE = generateKeccak256(
["string"],
["UNPAUSE_INITIATE_TOKEN_BRIDGING_ROLE"],
{ encodePacked: true },
);
export const UNPAUSE_COMPLETE_TOKEN_BRIDGING_ROLE = generateKeccak256(
["string"],
["UNPAUSE_COMPLETE_TOKEN_BRIDGING_ROLE"],
{ encodePacked: true },
);
export const SET_REMOTE_TOKENBRIDGE_ROLE = generateKeccak256(["string"], ["SET_REMOTE_TOKENBRIDGE_ROLE"], {
encodePacked: true,
});
export const SET_RESERVED_TOKEN_ROLE = generateKeccak256(["string"], ["SET_RESERVED_TOKEN_ROLE"], {
encodePacked: true,
});
export const REMOVE_RESERVED_TOKEN_ROLE = generateKeccak256(["string"], ["REMOVE_RESERVED_TOKEN_ROLE"], {
encodePacked: true,
});
export const SET_CUSTOM_CONTRACT_ROLE = generateKeccak256(["string"], ["SET_CUSTOM_CONTRACT_ROLE"], {
encodePacked: true,
});
export const SET_MESSAGE_SERVICE_ROLE = generateKeccak256(["string"], ["SET_MESSAGE_SERVICE_ROLE"], {
encodePacked: true,
});

export const BASE_ROLES = [PAUSE_ALL_ROLE, UNPAUSE_ALL_ROLE];

export const LINEA_ROLLUP_ROLES = [
...BASE_ROLES,
VERIFIER_SETTER_ROLE,
VERIFIER_UNSETTER_ROLE,
RATE_LIMIT_SETTER_ROLE,
USED_RATE_LIMIT_RESETTER_ROLE,
PAUSE_L1_L2_ROLE,
PAUSE_L2_L1_ROLE,
UNPAUSE_L1_L2_ROLE,
UNPAUSE_L2_L1_ROLE,
PAUSE_BLOB_SUBMISSION_ROLE,
UNPAUSE_BLOB_SUBMISSION_ROLE,
PAUSE_FINALIZE_WITHPROOF_ROLE,
UNPAUSE_FINALIZE_WITHPROOF_ROLE,
];

export const L2_MESSAGE_SERVICE_ROLES = [
...BASE_ROLES,
MINIMUM_FEE_SETTER_ROLE,
RATE_LIMIT_SETTER_ROLE,
USED_RATE_LIMIT_RESETTER_ROLE,
PAUSE_L1_L2_ROLE,
PAUSE_L2_L1_ROLE,
UNPAUSE_L1_L2_ROLE,
UNPAUSE_L2_L1_ROLE,
L1_L2_MESSAGE_SETTER_ROLE,
];

export const TOKEN_BRIDGE_ROLES = [
...BASE_ROLES,
SET_MESSAGE_SERVICE_ROLE,
SET_REMOTE_TOKENBRIDGE_ROLE,
SET_RESERVED_TOKEN_ROLE,
REMOVE_RESERVED_TOKEN_ROLE,
SET_CUSTOM_CONTRACT_ROLE,
PAUSE_INITIATE_TOKEN_BRIDGING_ROLE,
UNPAUSE_INITIATE_TOKEN_BRIDGING_ROLE,
PAUSE_COMPLETE_TOKEN_BRIDGING_ROLE,
UNPAUSE_COMPLETE_TOKEN_BRIDGING_ROLE,
];
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GetContractTypeFromFactory } from "../typechain-types/common";
import { ProxyAdmin, ProxyAdmin__factory, TransparentUpgradeableProxy__factory } from "../typechain-types";
import { ContractFactory, Overrides, Wallet, ethers } from "ethers";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { GetContractTypeFromFactory } from "../../typechain-types/common";
import { ProxyAdmin, ProxyAdmin__factory, TransparentUpgradeableProxy__factory } from "../../typechain-types";

export function getInitializerData(
contractInterface: ethers.Interface,
Expand Down
20 changes: 20 additions & 0 deletions contracts/common/helpers/encoding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ethers, AbiCoder } from "ethers";

export const encodeData = (types: string[], values: unknown[], packed?: boolean) => {
if (packed) {
return ethers.solidityPacked(types, values);
}
return AbiCoder.defaultAbiCoder().encode(types, values);
};

export function convertStringToPaddedHexBytes(strVal: string, paddedSize: number): string {
if (strVal.length > paddedSize) {
throw "Length is longer than padded size!";
}

const strBytes = ethers.toUtf8Bytes(strVal);
const bytes = ethers.zeroPadBytes(strBytes, paddedSize);
const bytes8Hex = ethers.hexlify(bytes);

return bytes8Hex;
}
32 changes: 32 additions & 0 deletions contracts/common/helpers/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export function getRequiredEnvVar(name: string): string {
const envValue = process.env[name];
if (!envValue) {
throw new Error(`Required environment variable "${name}" is missing or empty.`);
}
console.log(`Using environment variable ${name}=${envValue}`);
return envValue;
}

export function getEnvVarOrDefault(envVar: string, defaultValue: unknown) {
const envValue = process.env[envVar];

if (!envValue) {
console.log(`Using default ${envVar}`);
return defaultValue;
}

console.log(`Using provided ${envVar} environment variable`);
try {
const parsedValue = JSON.parse(envValue);
if (typeof parsedValue === "object" && !Array.isArray(parsedValue)) {
return parsedValue;
}

if (Array.isArray(parsedValue) && parsedValue.every((item) => typeof item === "object")) {
return parsedValue;
}
} catch (error) {
console.log(`Unable to parse ${envVar}, returning as string.`);
}
return envValue;
}
File renamed without changes.
1 change: 1 addition & 0 deletions contracts/common/helpers/general.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
5 changes: 5 additions & 0 deletions contracts/common/helpers/hashing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ethers } from "hardhat";
import { encodeData } from "./encoding";

export const generateKeccak256 = (types: string[], values: unknown[], opts: { encodePacked?: boolean }) =>
ethers.keccak256(encodeData(types, values, opts.encodePacked));
11 changes: 11 additions & 0 deletions contracts/common/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export * from "./roles";
export * from "./hashing";
export * from "./encoding";
export * from "./environment";
export * from "./auditedDeployVerifier";
export * from "./deployments";
export * from "./environmentHelper";
export * from "./readAddress";
export * from "./storeAddress";
export * from "./verifyContract";
export * from "./general";
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ export const getDeployedContractOnNetwork = async (
const data = fs.readFileSync(filePath, "utf-8");
return JSON.parse(data).address;
};

export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
33 changes: 33 additions & 0 deletions contracts/common/helpers/roles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function generateRoleAssignments(
roles: string[],
defaultAddress: string,
overrides: { role: string; addresses: string[] }[],
): { role: string; addressWithRole: string }[] {
const roleAssignments: { role: string; addressWithRole: string }[] = [];

const overridesMap = new Map<string, string[]>();
for (const override of overrides) {
overridesMap.set(override.role, override.addresses);
}

const allRolesSet = new Set<string>(roles);
for (const override of overrides) {
allRolesSet.add(override.role);
}

for (const role of allRolesSet) {
if (overridesMap.has(role)) {
const addresses = overridesMap.get(role);

if (addresses && addresses.length > 0) {
for (const addressWithRole of addresses) {
roleAssignments.push({ role, addressWithRole });
}
}
} else {
roleAssignments.push({ role, addressWithRole: defaultAddress });
}
}

return roleAssignments;
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,3 @@ export const getDeployedContractAddress = async (
}
return undefined;
};

export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { run } from "hardhat";
import { delay } from "./storeAddress";
import { delay } from "./general";

export async function tryVerifyContract(contractAddress: string) {
if (process.env.VERIFY_CONTRACT) {
Expand Down
File renamed without changes.
13 changes: 9 additions & 4 deletions contracts/contracts/LineaRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ contract LineaRollup is

bytes32 public constant VERIFIER_SETTER_ROLE = keccak256("VERIFIER_SETTER_ROLE");
bytes32 public constant VERIFIER_UNSETTER_ROLE = keccak256("VERIFIER_UNSETTER_ROLE");
bytes32 public constant FINALIZE_WITHOUT_PROOF_ROLE = keccak256("FINALIZE_WITHOUT_PROOF_ROLE");
bytes32 public constant GENESIS_SHNARF =
keccak256(
abi.encode(
Expand Down Expand Up @@ -103,6 +102,12 @@ contract LineaRollup is

__MessageService_init(_initializationData.rateLimitPeriodInSeconds, _initializationData.rateLimitAmountInWei);

/**
* @dev DEFAULT_ADMIN_ROLE is set for the security council explicitly,
* as the permissions init purposefully does not allow DEFAULT_ADMIN_ROLE to be set.
*/
_grantRole(DEFAULT_ADMIN_ROLE, _initializationData.defaultAdmin);

__Permissions_init(_initializationData.roleAddresses);

verifiers[0] = _initializationData.defaultVerifier;
Expand Down Expand Up @@ -206,7 +211,7 @@ contract LineaRollup is
BlobSubmissionData[] calldata _blobSubmissionData,
bytes32 _parentShnarf,
bytes32 _finalBlobShnarf
) external whenTypeAndGeneralNotPaused(BLOB_SUBMISSION_PAUSE_TYPE) onlyRole(OPERATOR_ROLE) {
) external whenTypeAndGeneralNotPaused(PauseType.BLOB_SUBMISSION) onlyRole(OPERATOR_ROLE) {
uint256 blobSubmissionLength = _blobSubmissionData.length;

if (blobSubmissionLength == 0) {
Expand Down Expand Up @@ -290,7 +295,7 @@ contract LineaRollup is
SubmissionDataV2 calldata _submissionData,
bytes32 _parentShnarf,
bytes32 _expectedShnarf
) external whenTypeAndGeneralNotPaused(CALLDATA_SUBMISSION_PAUSE_TYPE) onlyRole(OPERATOR_ROLE) {
) external whenTypeAndGeneralNotPaused(PauseType.CALLDATA_SUBMISSION) onlyRole(OPERATOR_ROLE) {
if (_submissionData.compressedData.length == 0) {
revert EmptySubmissionData();
}
Expand Down Expand Up @@ -465,7 +470,7 @@ contract LineaRollup is
bytes calldata _aggregatedProof,
uint256 _proofType,
FinalizationDataV2 calldata _finalizationData
) external whenTypeAndGeneralNotPaused(FINALIZATION_PAUSE_TYPE) onlyRole(OPERATOR_ROLE) {
) external whenTypeAndGeneralNotPaused(PauseType.FINALIZATION) onlyRole(OPERATOR_ROLE) {
if (_aggregatedProof.length == 0) {
revert ProofIsEmpty();
}
Expand Down
Loading

0 comments on commit efbec20

Please sign in to comment.