From 2b88a2b2db9438da465134a3bf55589cf1ba5420 Mon Sep 17 00:00:00 2001 From: Oba Date: Thu, 19 Sep 2024 16:26:15 +0200 Subject: [PATCH] feat: address aliasing --- .../src/L1L2Messaging/AddressAliasHelper.sol | 45 +++++++++++++++++++ .../src/L1L2Messaging/L1KakarotMessaging.sol | 8 +++- .../src/L1L2Messaging/MessageAppL1.sol | 9 ++++ .../src/L1L2Messaging/MessageAppL2.sol | 14 ++++-- .../L1L2Messaging/test_messaging.py | 30 ++++++++----- 5 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 solidity_contracts/src/L1L2Messaging/AddressAliasHelper.sol diff --git a/solidity_contracts/src/L1L2Messaging/AddressAliasHelper.sol b/solidity_contracts/src/L1L2Messaging/AddressAliasHelper.sol new file mode 100644 index 000000000..8788b2452 --- /dev/null +++ b/solidity_contracts/src/L1L2Messaging/AddressAliasHelper.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2019-2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// from https://github.com/ethereum-optimism/optimism/blob/a080bd23666513269ff241f1b7bc3bce74b6ad15/packages/contracts-bedrock/src/vendor/AddressAliasHelper.sol + +pragma solidity ^0.8.0; + +library AddressAliasHelper { + uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + + /// @notice Utility function that converts the address in the L1 that submitted a tx to + /// the inbox to the msg.sender viewed in the L2 + /// @param l1Address the address in the L1 that triggered the tx to L2 + /// @return l2Address L2 address as viewed in msg.sender + function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { + unchecked { + l2Address = address(uint160(l1Address) + offset); + } + } + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l2Address L2 address as viewed in msg.sender + /// @return l1Address the address in the L1 that triggered the tx to L2 + function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { + unchecked { + l1Address = address(uint160(l2Address) - offset); + } + } +} diff --git a/solidity_contracts/src/L1L2Messaging/L1KakarotMessaging.sol b/solidity_contracts/src/L1L2Messaging/L1KakarotMessaging.sol index 26fc1d293..71e426b00 100644 --- a/solidity_contracts/src/L1L2Messaging/L1KakarotMessaging.sol +++ b/solidity_contracts/src/L1L2Messaging/L1KakarotMessaging.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.27; -import "../starknet/IStarknetMessaging.sol"; +import {AddressAliasHelper} from "./AddressAliasHelper.sol"; +import {IStarknetMessaging} from "../starknet/IStarknetMessaging.sol"; interface IL1KakarotMessaging { function sendMessageToL2(address to, uint248 value, bytes memory data) external payable; @@ -9,9 +10,12 @@ interface IL1KakarotMessaging { } contract L1KakarotMessaging { + /// @dev The selector of the function to call on the L2 contract. uint256 public constant HANDLE_L1_MESSAGE_SELECTOR = uint256(keccak256("handle_l1_message")) % 2 ** 250; + /// @dev The Starknet messaging contract. IStarknetMessaging public immutable starknetMessaging; + /// @dev The address of the Kakarot contract on L2. uint256 public immutable kakarotAddress; constructor(address starknetMessaging_, uint256 kakarotAddress_) { @@ -28,7 +32,7 @@ contract L1KakarotMessaging { function sendMessageToL2(address to, uint248 value, bytes calldata data) external payable { uint256 totalLength = data.length + 4; uint256[] memory convertedData = new uint256[](totalLength); - convertedData[0] = uint256(uint160(msg.sender)); + convertedData[0] = uint256(uint160(AddressAliasHelper.applyL1ToL2Alias(msg.sender))); convertedData[1] = uint256(uint160(to)); convertedData[2] = uint256(value); convertedData[3] = data.length; diff --git a/solidity_contracts/src/L1L2Messaging/MessageAppL1.sol b/solidity_contracts/src/L1L2Messaging/MessageAppL1.sol index fdfd1b156..51d10eea5 100644 --- a/solidity_contracts/src/L1L2Messaging/MessageAppL1.sol +++ b/solidity_contracts/src/L1L2Messaging/MessageAppL1.sol @@ -37,6 +37,15 @@ contract MessageAppL1 { ); } + /// @notice Increases the counter inside the MessageAppL2 contract deployed on Kakarot from this contract only. + /// @dev Must be called with a value sufficient to pay for the L1 message fee. + /// @param l2AppAddress The address of the L2 contract to trigger. + function increaseL2AppCounterFromCounterPartOnly(address l2AppAddress) external payable { + _l1KakarotMessaging.sendMessageToL2{value: msg.value}( + l2AppAddress, 0, abi.encodeCall(MessageAppL2.increaseMessageCounterFromL1Contract, 1) + ); + } + /// @notice Manually consumes a message that was received from L2. /// @param fromAddress L2 address sending the message. /// @param payload Payload of the message used to verify the hash. diff --git a/solidity_contracts/src/L1L2Messaging/MessageAppL2.sol b/solidity_contracts/src/L1L2Messaging/MessageAppL2.sol index 5cc95f526..73d8597fc 100644 --- a/solidity_contracts/src/L1L2Messaging/MessageAppL2.sol +++ b/solidity_contracts/src/L1L2Messaging/MessageAppL2.sol @@ -2,13 +2,16 @@ pragma solidity >=0.7.0 <0.9.0; import {L2KakarotMessaging} from "./L2KakarotMessaging.sol"; +import {AddressAliasHelper} from "./AddressAliasHelper.sol"; contract MessageAppL2 { - L2KakarotMessaging immutable l2KakarotMessaging; + L2KakarotMessaging public immutable l2KakarotMessaging; + address public immutable l1ContractCounterPart; uint256 public receivedMessagesCounter; - constructor(address _l2KakarotMessaging) { - l2KakarotMessaging = L2KakarotMessaging(_l2KakarotMessaging); + constructor(address l2KakarotMessaging_, address l1ContractCounterPart_) { + l2KakarotMessaging = L2KakarotMessaging(l2KakarotMessaging_); + l1ContractCounterPart = l1ContractCounterPart_; } // @notice Sends a message to L1. @@ -20,4 +23,9 @@ contract MessageAppL2 { function increaseMessagesCounter(uint256 amount) external { receivedMessagesCounter += amount; } + + function increaseMessageCounterFromL1Contract(uint256 amount) external { + require(AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1ContractCounterPart, "ONLY_COUNTERPART_CONTRACT"); + receivedMessagesCounter += amount; + } } diff --git a/tests/end_to_end/L1L2Messaging/test_messaging.py b/tests/end_to_end/L1L2Messaging/test_messaging.py index 894f00a6e..419fad727 100644 --- a/tests/end_to_end/L1L2Messaging/test_messaging.py +++ b/tests/end_to_end/L1L2Messaging/test_messaging.py @@ -67,16 +67,6 @@ async def l2KakarotMessaging(kakarot): return l2KakarotMessaging -@pytest_asyncio.fixture(scope="session") -async def message_app_l2(owner, l2KakarotMessaging): - return await deploy( - "L1L2Messaging", - "MessageAppL2", - _l2KakarotMessaging=l2KakarotMessaging.address, - caller_eoa=owner.starknet_contract, - ) - - @pytest.fixture(scope="session") def message_app_l1(sn_messaging_local, l1_kakarot_messaging, kakarot): return deploy_on_l1( @@ -88,6 +78,16 @@ def message_app_l1(sn_messaging_local, l1_kakarot_messaging, kakarot): ) +@pytest_asyncio.fixture(scope="session") +async def message_app_l2(owner, l2KakarotMessaging, message_app_l1): + return await deploy( + "L1L2Messaging", + "MessageAppL2", + l2KakarotMessaging_=l2KakarotMessaging.address, + l1ContractCounterPart_=message_app_l1.address, + ) + + @pytest.fixture(scope="function") def wait_for_sn_messaging_local(sn_messaging_local): @@ -158,6 +158,16 @@ async def test_should_increment_counter_on_l2(self, message_app_l1, message_app_ msg_counter_after = await message_app_l2.receivedMessagesCounter() assert msg_counter_after == msg_counter_before + increment_value + async def test_should_apply_alias_from_l1(self, message_app_l1, message_app_l2): + msg_counter_before = await message_app_l2.receivedMessagesCounter() + increment_value = 1 + message_app_l1.increaseL2AppCounterFromCounterPartOnly( + message_app_l2.address, value=increment_value + ) + time.sleep(4) + msg_counter_after = await message_app_l2.receivedMessagesCounter() + assert msg_counter_after == msg_counter_before + increment_value + async def test_should_fail_unauthorized_message_sender( self, l1_kakarot_messaging, message_app_l1, message_app_l2 ):