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
528 changes: 264 additions & 264 deletions AllContractsHashes.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions configs/genesis/era/latest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
"minor": 31,
"patch": 0
},
"genesis_root": "0xd14523fbdf862f8c90ad9c8d4f15633f71685ca0b3a87395051af05a665521e4",
"genesis_root": "0xb784e2f4d87d5857ad32bf1cc8137706a870467fad8779126c3d4d0269498f85",
"genesis_rollup_leaf_index": 104,
"genesis_batch_commitment": "0x3bce47b3d7a1c55c8dd067392a1a084f2470265f5a71461bcf28fe927af7195d",
"bootloader_hash": "0x01000997783d068e1865ddd522fd4d99fb4385e5f32bd876b953fa1415a28112",
"default_aa_hash": "0x010005f9b417b825b2e9faaf9c727b7afd29c5a1b65269ff6f429320b7b87e4b",
"genesis_batch_commitment": "0xe9a8206e98cb0a9615b867dd463b1334464581f4b341035707bb43507f916950",
"bootloader_hash": "0x0100099b799188da75411d9c2815a792e60e031bc5f89c2938b6deced048ad83",
"default_aa_hash": "0x010005f9ab7430155bfafc451c3950faf16b97a6ad45f86e0bd41308e8b04685",
"evm_emulator_hash": "0x01000d8bae37b82f311186426184866498b357f41d7a02ced11f3e3fbfbacd63",
"prover": {
"recursion_scheduler_level_vk_hash": "0x1ffc56111a5cfaf5db387f6a31408ad20217e9bc1f31f2f5c1bd38b0d6d7968b",
Expand Down
8 changes: 4 additions & 4 deletions configs/genesis/era/latest.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
protocol_semantic_version = 133143986176
genesis_root = "0xd14523fbdf862f8c90ad9c8d4f15633f71685ca0b3a87395051af05a665521e4"
genesis_root = "0xb784e2f4d87d5857ad32bf1cc8137706a870467fad8779126c3d4d0269498f85"
genesis_rollup_leaf_index = 104
genesis_batch_commitment = "0x3bce47b3d7a1c55c8dd067392a1a084f2470265f5a71461bcf28fe927af7195d"
bootloader_hash = "0x01000997783d068e1865ddd522fd4d99fb4385e5f32bd876b953fa1415a28112"
default_aa_hash = "0x010005f9b417b825b2e9faaf9c727b7afd29c5a1b65269ff6f429320b7b87e4b"
genesis_batch_commitment = "0xe9a8206e98cb0a9615b867dd463b1334464581f4b341035707bb43507f916950"
bootloader_hash = "0x0100099b799188da75411d9c2815a792e60e031bc5f89c2938b6deced048ad83"
default_aa_hash = "0x010005f9ab7430155bfafc451c3950faf16b97a6ad45f86e0bd41308e8b04685"
evm_emulator_hash = "0x01000d8bae37b82f311186426184866498b357f41d7a02ced11f3e3fbfbacd63"

[prover]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,8 @@ contract GWAssetTracker is AssetTrackerBase, IGWAssetTracker {
// solhint-disable-next-line
_processInteropCall(_chainId, bundleHash, interopCall, interopBundle.destinationChainId);
}
// FIXME: this value can be zero leading to settlement failure.
bytes32 destinationChainBaseTokenAssetId = _bridgehub().baseTokenAssetId(interopBundle.destinationChainId);
_decreaseChainBalance(_chainId, destinationChainBaseTokenAssetId, totalBaseTokenAmount);
// We check on the InteropHandler of the destination chain that the `destinationBaseTokenAssetId` is the correct one
_decreaseChainBalance(_chainId, interopBundle.destinationBaseTokenAssetId, totalBaseTokenAmount);
interopBalanceChange[interopBundle.destinationChainId][bundleHash].baseTokenAmount = totalBaseTokenAmount;

// Return chargeable call count for settlement fee calculation.
Expand Down
2 changes: 2 additions & 0 deletions l1-contracts/contracts/common/Messaging.sol
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ enum CallStatus {
/// @dev A set of `InteropCall`s to send to another chain.
/// @param version Version of the InteropBundle.
/// @param destinationChainId ChainId of the target chain.
/// @param destinationBaseTokenAssetId Asset ID of the base token of the target chain.
/// @param interopBundleSalt Salt of the interopBundle. It's required to ensure that all bundles have distinct hashes.
/// It's equal to the keccak256(abi.encodePacked(senderOfTheBundle, NumberOfBundleSentByTheSender))
/// @param calls Array of InteropCall structs to execute.
Expand All @@ -258,6 +259,7 @@ struct InteropBundle {
bytes1 version;
uint256 sourceChainId;
uint256 destinationChainId;
bytes32 destinationBaseTokenAssetId;
bytes32 interopBundleSalt;
InteropCall[] calls;
BundleAttributes bundleAttributes;
Expand Down
4 changes: 4 additions & 0 deletions l1-contracts/contracts/core/bridgehub/L1BridgehubErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ error ChainBatchRootAlreadyExists(uint256 chainId, uint256 batchNumber);
error ChainBatchRootZero();
// 0x65e8a019
error ChainExists();
// 0x824e4e26
error ChainsSettlementLayerMismatch(uint256 chainToRegisterSL, uint256 chainRegisteredOnSL);
// 0x1ed6c04f
error ChainsSettlingOnL1();
// 0x5d03f19d
error CurrentBatchNumberAlreadySet();
// 0x68d91b49
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {L2_BRIDGEHUB_ADDR} from "../../common/l2-helpers/L2ContractAddresses.sol
import {TWO_BRIDGES_MAGIC_VALUE} from "../../common/Config.sol";

import {Unauthorized, UnsupportedEncodingVersion} from "../../common/L1ContractErrors.sol";
import {ChainAlreadyRegistered, NoEthAllowed, ZKChainNotRegistered} from "../bridgehub/L1BridgehubErrors.sol";
import {ChainAlreadyRegistered, ChainsSettlingOnL1, ChainsSettlementLayerMismatch, NoEthAllowed, ZKChainNotRegistered} from "../bridgehub/L1BridgehubErrors.sol";
import {IL2Bridgehub} from "../bridgehub/IL2Bridgehub.sol";

/// @dev The encoding version of the data.
Expand Down Expand Up @@ -57,10 +57,13 @@ contract ChainRegistrationSender is
/// @notice used to register a chain for interop via a service transaction.abi
/// @notice this is provided for ease of use, base tokens does not have to be provided.
/// @notice to prevent spamming, we only allow this to be called once.
/// @notice only chains that are settling on the same settlement layer may be registered.
/// @param chainToBeRegistered the chain to be registered
/// @param chainRegisteredOn the chain to register on
function registerChain(uint256 chainToBeRegistered, uint256 chainRegisteredOn) external {
require(!chainRegisteredOnChain[chainToBeRegistered][chainRegisteredOn], ChainAlreadyRegistered());
_checkSettlementLayers(chainToBeRegistered, chainRegisteredOn);

chainRegisteredOnChain[chainToBeRegistered][chainRegisteredOn] = true;

IMailbox chainRegisteredOnAddress = IMailbox(BRIDGE_HUB.getZKChain(chainRegisteredOn));
Expand All @@ -76,7 +79,7 @@ contract ChainRegistrationSender is
/// @notice this is can be called by anyone (via the bridgehub), but baseTokens need to be provided.
// slither-disable-next-line locked-ether
function bridgehubDeposit(
uint256,
uint256 chainRegisteredOn,
address,
uint256,
bytes calldata _data
Expand All @@ -94,6 +97,8 @@ contract ChainRegistrationSender is
if (chainToBeRegisteredAddress == address(0)) {
revert ZKChainNotRegistered();
}
_checkSettlementLayers(chainToBeRegistered, chainRegisteredOn);

request = L2TransactionRequestTwoBridgesInner({
magicValue: TWO_BRIDGES_MAGIC_VALUE,
l2Contract: L2_BRIDGEHUB_ADDR,
Expand All @@ -115,6 +120,20 @@ contract ChainRegistrationSender is
return abi.encodeCall(IL2Bridgehub.registerChainForInterop, (chainToBeRegistered, baseTokenAssetId));
}

/// @notice Checks that both chains are settling on the same settlement layer, which must not be the L1
/// @param chainToBeRegistered the chain to be registered
/// @param chainRegisteredOn the chain to register on
function _checkSettlementLayers(uint256 chainToBeRegistered, uint256 chainRegisteredOn) internal view {
uint256 chainToBeRegisteredSettlementLayer = BRIDGE_HUB.settlementLayer(chainToBeRegistered);
uint256 chainRegisteredOnSettlementLayer = BRIDGE_HUB.settlementLayer(chainRegisteredOn);
if (chainToBeRegisteredSettlementLayer != chainRegisteredOnSettlementLayer) {
revert ChainsSettlementLayerMismatch(chainToBeRegisteredSettlementLayer, chainRegisteredOnSettlementLayer);
}
if (chainToBeRegisteredSettlementLayer == block.chainid) {
revert ChainsSettlingOnL1();
}
}

/// @inheritdoc IL1CrossChainSender
/// @notice This function is not used for ChainRegistrationSender, since we do not need to support failed L1->L2 transactions.
function bridgehubConfirmL2Transaction(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external override {}
Expand Down
51 changes: 33 additions & 18 deletions l1-contracts/contracts/interop/InteropCenter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {BUNDLE_IDENTIFIER, BalanceChange, BundleAttributes, CallAttributes, INTE
import {MsgValueMismatch, NotL2ToL2, Unauthorized, ZeroAddress} from "../common/L1ContractErrors.sol";
import {NotInGatewayMode} from "../core/bridgehub/L1BridgehubErrors.sol";

import {AttributeAlreadySet, AttributeViolatesRestriction, DestinationChainNotRegistered, IndirectCallValueMismatch, InteroperableAddressChainReferenceNotEmpty, InteroperableAddressNotEmpty, FeeWithdrawalFailed, ThisChainNotRegisteredForInterop, ZKTokenNotAvailable} from "./InteropErrors.sol";
import {AttributeAlreadySet, AttributeViolatesRestriction, DestinationChainNotRegistered, IndirectCallValueMismatch, InteroperableAddressChainReferenceNotEmpty, InteroperableAddressNotEmpty, FeeWithdrawalFailed, ZKTokenNotAvailable} from "./InteropErrors.sol";

import {IERC7786GatewaySource} from "./IERC7786GatewaySource.sol";
import {IERC7786Attributes} from "./IERC7786Attributes.sol";
Expand Down Expand Up @@ -325,26 +325,20 @@ contract InteropCenter is
/// @param _callCount Number of calls in the bundle for per-call fee calculation.
function _ensureCorrectTotalValue(
uint256 _destinationChainId,
bytes32 _destinationBaseTokenAssetId,
uint256 _totalBurnedCallsValue,
uint256 _totalIndirectCallsValue,
bool _useFixedFee,
uint256 _callCount
) internal {
// Note, that non-zero `destinationChainBaseTokenAssetId` does not mean that the destination chain
// actually settles on the GW or is able to receive the message.
// However, this value is provided from L1 by chain asset handler, so it can be trusted to be correct.
bytes32 destinationChainBaseTokenAssetId = L2_BRIDGEHUB.baseTokenAssetId(_destinationChainId);
require(destinationChainBaseTokenAssetId != bytes32(0), DestinationChainNotRegistered(_destinationChainId));
bytes32 thisChainBaseTokenAssetId = L2_BRIDGEHUB.baseTokenAssetId(block.chainid);
bytes32 thisChainBaseTokenAssetId = L2_NATIVE_TOKEN_VAULT.BASE_TOKEN_ASSET_ID();

// Calculate protocol fee - only charge base token fee if not using fixed ZK fees.
// Fee is charged per-call.
uint256 protocolFee = _useFixedFee ? 0 : interopProtocolFee * _callCount;

require(thisChainBaseTokenAssetId != bytes32(0), ThisChainNotRegisteredForInterop(block.chainid));

// We burn the value that is passed along the bundle here, on source chain.
if (destinationChainBaseTokenAssetId == thisChainBaseTokenAssetId) {
if (_destinationBaseTokenAssetId == thisChainBaseTokenAssetId) {
uint256 expectedValue = _totalBurnedCallsValue + _totalIndirectCallsValue + protocolFee;
require(msg.value == expectedValue, MsgValueMismatch(expectedValue, msg.value));

Expand All @@ -361,7 +355,7 @@ contract InteropCenter is
if (_totalBurnedCallsValue > 0) {
IAssetRouterShared(L2_ASSET_ROUTER_ADDR).bridgehubDepositBaseToken(
_destinationChainId,
destinationChainBaseTokenAssetId,
_destinationBaseTokenAssetId,
msg.sender,
_totalBurnedCallsValue
);
Expand Down Expand Up @@ -391,10 +385,13 @@ contract InteropCenter is
require(L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT.currentSettlementLayerChainId() != L1_CHAIN_ID, NotInGatewayMode());

// Form an InteropBundle.
bytes32 destinationBaseTokenAssetId = L2_BRIDGEHUB.baseTokenAssetId(_destinationChainId);
require(destinationBaseTokenAssetId != bytes32(0), DestinationChainNotRegistered(_destinationChainId));
InteropBundle memory bundle = InteropBundle({
version: INTEROP_BUNDLE_VERSION,
sourceChainId: block.chainid,
destinationChainId: _destinationChainId,
destinationBaseTokenAssetId: destinationBaseTokenAssetId,
interopBundleSalt: keccak256(abi.encodePacked(msg.sender, interopBundleNonce[msg.sender])),
calls: new InteropCall[](_callStarters.length),
bundleAttributes: _bundleAttributes
Expand Down Expand Up @@ -433,6 +430,7 @@ contract InteropCenter is
// solhint-disable-next-line
_ensureCorrectTotalValue(
bundle.destinationChainId,
bundle.destinationBaseTokenAssetId,
totalBurnedCallsValue,
totalIndirectCallsValue,
_bundleAttributes.useFixedFee,
Expand All @@ -449,21 +447,38 @@ contract InteropCenter is
bundleHash = InteropDataEncoding.encodeInteropBundleHash(block.chainid, interopBundleBytes);
}

// Emit ERC-7786 MessageSent event for each call in the bundle
for (uint256 i = 0; i < callStartersLength; ++i) {
InteropCall memory currentCall = bundle.calls[i];
_emitMessageSent({
_calls: bundle.calls,
_destinationChainId: _destinationChainId,
_bundleHash: bundleHash,
_callStarters: _callStarters,
_originalCallAttributes: _originalCallAttributes
});

// Emit event stating that the bundle was sent out successfully.
emit InteropBundleSent(msgHash, bundleHash, bundle);
}

/// @notice Emits ERC-7786 MessageSent events for each call in a bundle.
function _emitMessageSent(
InteropCall[] memory _calls,
uint256 _destinationChainId,
bytes32 _bundleHash,
InteropCallStarterInternal[] memory _callStarters,
bytes[][] memory _originalCallAttributes
) internal {
uint256 callsLength = _callStarters.length;
for (uint256 i = 0; i < callsLength; ++i) {
InteropCall memory currentCall = _calls[i];
emit MessageSent({
sendId: keccak256(abi.encodePacked(bundleHash, i)),
sendId: keccak256(abi.encodePacked(_bundleHash, i)),
sender: InteroperableAddress.formatEvmV1(block.chainid, currentCall.from),
recipient: InteroperableAddress.formatEvmV1(_destinationChainId, currentCall.to),
payload: _callStarters[i].data,
value: _callStarters[i].callAttributes.interopCallValue,
attributes: _originalCallAttributes[i]
});
}

// Emit event stating that the bundle was sent out successfully.
emit InteropBundleSent(msgHash, bundleHash, bundle);
}

function _processCallStarter(
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/contracts/interop/InteropErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ error InvalidInteropCallVersion();
error InteropRootAlreadyExists();
// 0x32c2e156
error MessageNotIncluded();
// 0x95eb7603
error ThisChainNotRegisteredForInterop(uint256 chainId);
// 0x2f59bd0d
error SidesLengthNotOne();
// 0x89fd2c76
Expand All @@ -45,6 +43,8 @@ error UnbundlingNotAllowed(bytes32 bundleHash, bytes callerAddress, bytes unbund
error WrongCallStatusLength(uint256 bundleCallsLength, uint256 providedCallStatusLength);
// 0x4534e972
error WrongDestinationChainId(bytes32 bundleHash, uint256 expected, uint256 actual);
// 0xb99d46dc
error WrongDestinationBaseTokenAssetId(bytes32 bundleHash, bytes32 expected, bytes32 actual);
// 0x534ab1b2
error WrongSourceChainId(bytes32 bundleHash, uint256 expected, uint256 actual);
// 0x92196069
Expand Down
20 changes: 18 additions & 2 deletions l1-contracts/contracts/interop/InteropHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ pragma solidity ^0.8.24;

import {InteroperableAddress} from "../vendor/draft-InteroperableAddress.sol";

import {L2_BASE_TOKEN_SYSTEM_CONTRACT, L2_INTEROP_CENTER_ADDR, L2_MESSAGE_VERIFICATION, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT, L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT, L2_COMPLEX_UPGRADER_ADDR} from "../common/l2-helpers/L2ContractInterfaces.sol";
import {L2_BASE_TOKEN_SYSTEM_CONTRACT, L2_INTEROP_CENTER_ADDR, L2_NATIVE_TOKEN_VAULT, L2_MESSAGE_VERIFICATION, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT, L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT, L2_COMPLEX_UPGRADER_ADDR} from "../common/l2-helpers/L2ContractInterfaces.sol";
import {IInteropHandler} from "./IInteropHandler.sol";
import {BUNDLE_IDENTIFIER, INTEROP_BUNDLE_VERSION, INTEROP_CALL_VERSION, BundleStatus, CallStatus, InteropBundle, InteropCall, MessageInclusionProof} from "../common/Messaging.sol";
import {IERC7786Recipient} from "./IERC7786Recipient.sol";
import {ReentrancyGuard} from "../common/ReentrancyGuard.sol";
import {InteropDataEncoding} from "./InteropDataEncoding.sol";
import {BundleAlreadyProcessed, CallAlreadyExecuted, CallNotExecutable, CanNotUnbundle, ExecutingNotAllowed, MessageNotIncluded, UnauthorizedMessageSender, UnbundlingNotAllowed, WrongCallStatusLength, WrongDestinationChainId, WrongSourceChainId, InvalidInteropBundleVersion, InvalidInteropCallVersion} from "./InteropErrors.sol";
import {BundleAlreadyProcessed, CallAlreadyExecuted, CallNotExecutable, CanNotUnbundle, ExecutingNotAllowed, MessageNotIncluded, UnauthorizedMessageSender, UnbundlingNotAllowed, WrongCallStatusLength, WrongDestinationChainId, WrongDestinationBaseTokenAssetId, WrongSourceChainId, InvalidInteropBundleVersion, InvalidInteropCallVersion} from "./InteropErrors.sol";
import {InvalidSelector, Unauthorized} from "../common/L1ContractErrors.sol";
import {NotInGatewayMode} from "../core/bridgehub/L1BridgehubErrors.sol";

Expand Down Expand Up @@ -62,6 +62,13 @@ contract InteropHandler is IInteropHandler, ReentrancyGuard {
WrongDestinationChainId(bundleHash, interopBundle.destinationChainId, block.chainid)
);

// Verify that the destination base token asset ID of the bundle is equal to the base token asset ID of the chain
bytes32 baseTokenAssetId = L2_NATIVE_TOKEN_VAULT.BASE_TOKEN_ASSET_ID();
require(
interopBundle.destinationBaseTokenAssetId == baseTokenAssetId,
WrongDestinationBaseTokenAssetId(bundleHash, baseTokenAssetId, interopBundle.destinationBaseTokenAssetId)
);

// If the execution address is not specified then the execution is permissionless.
if (interopBundle.bundleAttributes.executionAddress.length != 0) {
(uint256 executionChainId, address executionAddress) = InteroperableAddress.parseEvmV1(
Expand Down Expand Up @@ -137,6 +144,13 @@ contract InteropHandler is IInteropHandler, ReentrancyGuard {
WrongDestinationChainId(bundleHash, interopBundle.destinationChainId, block.chainid)
);

// Verify that the destination base token asset ID of the bundle is equal to the base token asset ID of the chain
bytes32 baseTokenAssetId = L2_NATIVE_TOKEN_VAULT.BASE_TOKEN_ASSET_ID();
require(
interopBundle.destinationBaseTokenAssetId == baseTokenAssetId,
WrongDestinationBaseTokenAssetId(bundleHash, baseTokenAssetId, interopBundle.destinationBaseTokenAssetId)
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistic, but repeated fromlines 65-70

Moving to a separate function won't add much, as variables will have to be passed, but might remove repetition

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some level of code duplication on InteropHandler (eg, checks above this one), probably worth to refactor it but imo better to do so on a separate PR.


// If the bundle was already fully executed or unbundled, we revert stating that it was processed already.
require(status == BundleStatus.Unreceived, BundleAlreadyProcessed(bundleHash));

Expand All @@ -150,6 +164,8 @@ contract InteropHandler is IInteropHandler, ReentrancyGuard {
bytes memory _bundle,
CallStatus[] calldata _providedCallStatus
) public {
require(L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT.currentSettlementLayerChainId() != L1_CHAIN_ID, NotInGatewayMode());

// Decode the bundle data, calculate its hash and get the current status of the bundle.
(InteropBundle memory interopBundle, bytes32 bundleHash, BundleStatus status) = _getBundleData(
_bundle,
Expand Down
Loading
Loading