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: Refactor pause manager structure #126

Merged
merged 14 commits into from
Oct 4, 2024
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
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,
];
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;
}
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
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
Loading