Skip to content
Open
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
12 changes: 12 additions & 0 deletions contracts/src/_testing/unit/rollup/TestLineaRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ contract TestLineaRollup is LineaRollup, CalldataBlobAcceptor {
super.renounceRole(_role, _account);
}

function setSlotValue(uint256 _slot, uint256 _value) external {
assembly {
sstore(_slot, _value)
}
}

function getSlotValue(uint256 _slot) external view returns (uint256 slotValue) {
assembly {
slotValue := sload(_slot)
}
}

function addL2MerkleRoots(bytes32[] calldata _newRoot, uint256 _treeDepth) external onlyRole(DEFAULT_ADMIN_ROLE) {
_addL2MerkleRoots(_newRoot, _treeDepth);
}
Expand Down
21 changes: 11 additions & 10 deletions contracts/src/bridging/token/TokenBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
pragma solidity 0.8.33;

import { TokenBridgeBase } from "./TokenBridgeBase.sol";
import { InitializationVersionCheck } from "../../common/InitializationVersionCheck.sol";

/**
* @title Linea Canonical Token Bridge
* @notice Contract to manage cross-chain ERC-20 bridging.
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
*/
contract TokenBridge is TokenBridgeBase {
contract TokenBridge is InitializationVersionCheck, TokenBridgeBase {
/// @dev Disable constructor for safety
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand All @@ -29,22 +30,22 @@ contract TokenBridge is TokenBridgeBase {
nonZeroAddress(_initializationData.tokenBeacon)
nonZeroChainId(_initializationData.sourceChainId)
nonZeroChainId(_initializationData.targetChainId)
initializer
onlyInitializedVersion(0)
reinitializer(3)
{
__TokenBridge_init(_initializationData);
}

/**
* @notice Reinitializes TokenBridge and clears the old reentry slot value.
*/
function reinitializeV2() external reinitializer(2) {
address proxyAdmin;
assembly {
proxyAdmin := sload(PROXY_ADMIN_SLOT)
}
require(msg.sender == proxyAdmin, CallerNotProxyAdmin());

assembly {
function reinitializeV3() external reinitializer(3) nonReentrant {
uint256 oldReentrancyGuardEntered = 2;
assembly ("memory-safe") {
if eq(sload(1), oldReentrancyGuardEntered) {
mstore(0x00, 0x37ed32e8) //ReentrantCall.selector;
revert(0x1c, 0x04)
}
sstore(1, 0)
}
}
Expand Down
7 changes: 0 additions & 7 deletions contracts/src/bridging/token/TokenBridgeBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ abstract contract TokenBridgeBase is
using EfficientLeftRightKeccak for *;
using SafeERC20Upgradeable for IERC20Upgradeable;

/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* used to validate on the proxy admin can reinitialize the contract.
*/
bytes32 internal constant PROXY_ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

/// @notice Role used for setting the message service address.
bytes32 public constant SET_MESSAGE_SERVICE_ROLE = keccak256("SET_MESSAGE_SERVICE_ROLE");

Expand Down
23 changes: 23 additions & 0 deletions contracts/src/common/InitializationVersionCheck.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.33;

import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { IGenericErrors } from "../interfaces/IGenericErrors.sol";

/**
* @title Modifier to guard initialize functions against re-initialization.
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
*/
abstract contract InitializationVersionCheck is Initializable, IGenericErrors {
/**
* @dev Reverts if the contract's initialized version does not match the expected version.
* @param _expectedVersion The exact initialized version required.
*/
modifier onlyInitializedVersion(uint8 _expectedVersion) {
if (_getInitializedVersion() != _expectedVersion) {
revert InitializedVersionWrong(_expectedVersion, _getInitializedVersion());
}
_;
}
}
6 changes: 4 additions & 2 deletions contracts/src/interfaces/IGenericErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ interface IGenericErrors {
error NoEthSent();

/**
* @dev Thrown when the caller is not the ProxyAdmin.
* @dev Thrown when an initialize function is called on an already initialized contract with the wrong version.
* @param expected The expected initialized version.
* @param actual The actual initialized version.
*/
error CallerNotProxyAdmin();
error InitializedVersionWrong(uint256 expected, uint256 actual);
}
20 changes: 10 additions & 10 deletions contracts/src/messaging/l2/L2MessageService.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.33;
import { L2MessageServiceBase } from "./L2MessageServiceBase.sol";
import { InitializationVersionCheck } from "../../common/InitializationVersionCheck.sol";
/**
* @title Contract to manage cross-chain messaging on L2.
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
*/
contract L2MessageService is L2MessageServiceBase {
contract L2MessageService is InitializationVersionCheck, L2MessageServiceBase {
/// @dev Total contract storage is 50 slots with the gap below.
/// @dev Keep 50 free storage slots for future implementation updates to avoid storage collision.
uint256[50] private __gap_L2MessageService;
Expand All @@ -32,7 +33,7 @@ contract L2MessageService is L2MessageServiceBase {
RoleAddress[] calldata _roleAddresses,
PauseTypeRole[] calldata _pauseTypeRoleAssignments,
PauseTypeRole[] calldata _unpauseTypeRoleAssignments
) external virtual initializer {
) external virtual onlyInitializedVersion(0) reinitializer(3) {
__L2MessageService_init(
_rateLimitPeriod,
_rateLimitAmount,
Expand All @@ -46,14 +47,13 @@ contract L2MessageService is L2MessageServiceBase {
/**
* @notice Reinitializes the L2MessageService and clears the old reentry slot value.
*/
function reinitializeV3() external reinitializer(3) {
address proxyAdmin;
assembly {
proxyAdmin := sload(PROXY_ADMIN_SLOT)
}
require(msg.sender == proxyAdmin, CallerNotProxyAdmin());

assembly {
function reinitializeV3() external reinitializer(3) nonReentrant {
uint256 oldReentrancyGuardEntered = 2;
assembly ("memory-safe") {
if eq(sload(177), oldReentrancyGuardEntered) {
mstore(0x00, 0x37ed32e8) //ReentrantCall.selector;
revert(0x1c, 0x04)
}
sstore(177, 0)
}
}
Expand Down
7 changes: 0 additions & 7 deletions contracts/src/messaging/l2/L2MessageServiceBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@ abstract contract L2MessageServiceBase is
L2MessageManager,
PermissionsManager
{
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* used to validate on the proxy admin can reinitialize the contract.
*/
bytes32 internal constant PROXY_ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

/// @dev This is the ABI version and not the reinitialize version.
string private constant _CONTRACT_VERSION = "1.0";

Expand Down
12 changes: 4 additions & 8 deletions contracts/src/rollup/LineaRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import { LivenessRecovery } from "./LivenessRecovery.sol";
import { IPermissionsManager } from "../security/access/interfaces/IPermissionsManager.sol";
import { IPauseManager } from "../security/pausing/interfaces/IPauseManager.sol";
import { LineaRollupYieldExtension } from "./LineaRollupYieldExtension.sol";
import { InitializationVersionCheck } from "../common/InitializationVersionCheck.sol";

/**
* @title Contract to manage cross-chain messaging on L1, L2 data submission, and rollup proof verification.
* @author ConsenSys Software Inc.
* @custom:security-contact security-report@linea.build
*/
contract LineaRollup is
InitializationVersionCheck,
LineaRollupBase,
LineaRollupYieldExtension,
LivenessRecovery,
Expand All @@ -41,7 +43,7 @@ contract LineaRollup is
BaseInitializationData calldata _initializationData,
address _livenessRecoveryOperator,
address _yieldManager
) external initializer {
) external onlyInitializedVersion(0) reinitializer(8) {
bytes32 genesisShnarf = _computeShnarf(
EMPTY_HASH,
EMPTY_HASH,
Expand All @@ -64,13 +66,7 @@ contract LineaRollup is
IPermissionsManager.RoleAddress[] calldata _roleAddresses,
IPauseManager.PauseTypeRole[] calldata _pauseTypeRoles,
IPauseManager.PauseTypeRole[] calldata _unpauseTypeRoles
) external reinitializer(8) {
address proxyAdmin;
assembly {
proxyAdmin := sload(PROXY_ADMIN_SLOT)
}
require(msg.sender == proxyAdmin, CallerNotProxyAdmin());

) external reinitializer(8) nonReentrant {
__PauseManager_init(_pauseTypeRoles, _unpauseTypeRoles);
__Permissions_init(_roleAddresses);

Expand Down
9 changes: 1 addition & 8 deletions contracts/src/rollup/LineaRollupBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,6 @@ abstract contract LineaRollupBase is
ILineaRollupBase,
IProvideShnarf
{
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* used to validate on the proxy admin can reinitialize the contract.
*/
bytes32 internal constant PROXY_ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

using EfficientLeftRightKeccak for *;

/// @notice The role required to set/add proof verifiers by type.
Expand Down Expand Up @@ -148,7 +141,7 @@ abstract contract LineaRollupBase is

shnarfProvider = IProvideShnarf(shnarfProviderAddress);

emit LineaRollupBaseInitialized(_initializationData);
emit LineaRollupBaseInitialized(_initializationData, _genesisShnarf);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ contract LineaRollupClaimingV2 is LineaRollupBase, Eip4844BlobAcceptor, Calldata
* @notice Reinitializes LineaRollup and sets the _shnarfProvider to itself.
*/
function reinitializeV8() external reinitializer(8) {
address proxyAdmin;
assembly {
proxyAdmin := sload(PROXY_ADMIN_SLOT)
}
require(msg.sender == proxyAdmin, CallerNotProxyAdmin());

shnarfProvider = IProvideShnarf(address(this));
}
}
7 changes: 6 additions & 1 deletion contracts/src/rollup/interfaces/ILineaRollupBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ interface ILineaRollupBase {
bytes32 finalStateRootHash
);

event LineaRollupBaseInitialized(BaseInitializationData InitializationData);
/**
* @notice Emitted when the LineaRollup contract is initialized.
* @param InitializationData The initialization data.
* @param genesisShnarf The genesis shnarf.
*/
event LineaRollupBaseInitialized(BaseInitializationData InitializationData, bytes32 genesisShnarf);

/**
* @dev Thrown when finalizationData.l1RollingHash does not exist on L1 (Feedback loop).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,37 @@ pragma solidity ^0.8.33;
* @custom:security-contact security-report@linea.build
*/
abstract contract TransientStorageReentrancyGuardUpgradeable {
uint256 transient TRANSIENT_ENTERED;
/// @dev The key in the EVM transient storage where the reentrancy guard is stored.
// bytes32(uint256(keccak256("eip1967.reentrancy.guard.transient.key")) - 1);
bytes32 private constant REENTRANCY_GUARD_TRANSIENT_KEY =
0x084edf88d5959696dcc7aab5c8674a33a1ef78f37dda21b782ed03bddb22ade4;
uint256 private constant NOT_ENTERED = 0;
uint256 private constant ENTERED = 1;

/// @dev The error that is thrown when a reentrant call is detected.
error ReentrantCall();

/// @dev This gap is used to not shift down the storage layout after removing the OpenZeppelin ReentrancyGuardUpgradeable contract.
uint256[50] private __gap_ReentrancyGuardUpgradeable;

/**
* @notice Checks reentrancy and if not reentrant sets the transient reentry flag.
* @dev The selector for the ReentrantCall error.
* ReentrancyGuardUpgradeable.ReentrantCall.selector = 0x37ed32e8
*/
modifier nonReentrant() {
if (TRANSIENT_ENTERED == ENTERED) {
revert ReentrantCall();
assembly ("memory-safe") {
if eq(tload(REENTRANCY_GUARD_TRANSIENT_KEY), ENTERED) {
mstore(0x00, 0x37ed32e8) //ReentrantCall.selector;
revert(0x1c, 0x04)
}

tstore(REENTRANCY_GUARD_TRANSIENT_KEY, ENTERED)
}
TRANSIENT_ENTERED = ENTERED;

_;
TRANSIENT_ENTERED = NOT_ENTERED;
assembly ("memory-safe") {
tstore(REENTRANCY_GUARD_TRANSIENT_KEY, NOT_ENTERED)
}
}
}
Loading
Loading