diff --git a/l1-contracts/src/core/libraries/Hash.sol b/l1-contracts/src/core/libraries/Hash.sol new file mode 100644 index 000000000000..4f3be42a996b --- /dev/null +++ b/l1-contracts/src/core/libraries/Hash.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Aztec Labs. +pragma solidity >=0.8.18; + +import {Constants} from "./Constants.sol"; +import {DataStructures} from "./DataStructures.sol"; + +/** + * @title Hash library + * @author Aztec + * @notice A small library of functions to compute hashes for data structures and convert to field elements + */ +library Hash { + /** + * @notice Computes the sha256 hash of the L1 to L2 message and converts it to a field element + * @param _message - The L1 to L2 message to hash + * @return The hash of the provided message as a field element + */ + function sha256ToField(DataStructures.L1ToL2Msg memory _message) internal pure returns (bytes32) { + return sha256ToField( + abi.encode( + _message.sender, + _message.recipient, + _message.content, + _message.secretHash, + _message.deadline, + _message.fee + ) + ); + } + + /** + * @notice Computes the sha256 hash of the L2 to L1 message and converts it to a field element + * @param _message - The L2 to L1 message to hash + * @return The hash of the provided message as a field element + */ + function sha256ToField(DataStructures.L2ToL1Msg memory _message) internal pure returns (bytes32) { + return sha256ToField(abi.encode(_message.sender, _message.recipient, _message.content)); + } + + /** + * @notice Computes the sha256 hash of the provided data and converts it to a field element + * @dev Using modulo to convert the hash to a field element. + * @param _data - The bytes to hash + * @return The hash of the provided data as a field element + */ + function sha256ToField(bytes memory _data) internal pure returns (bytes32) { + return bytes32(uint256(sha256(_data)) % Constants.P); + } +} diff --git a/l1-contracts/src/core/messagebridge/Inbox.sol b/l1-contracts/src/core/messagebridge/Inbox.sol index bdd311c29543..f80ecf3aed96 100644 --- a/l1-contracts/src/core/messagebridge/Inbox.sol +++ b/l1-contracts/src/core/messagebridge/Inbox.sol @@ -3,8 +3,8 @@ pragma solidity >=0.8.18; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; -import {Constants} from "@aztec/core/libraries/Constants.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; +import {Hash} from "@aztec/core/libraries/Hash.sol"; import {MessageBox} from "@aztec/core/libraries/MessageBox.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {IRegistry} from "@aztec/core/interfaces/messagebridge/IRegistry.sol"; @@ -16,6 +16,7 @@ import {IRegistry} from "@aztec/core/interfaces/messagebridge/IRegistry.sol"; */ contract Inbox is IInbox { using MessageBox for mapping(bytes32 entryKey => DataStructures.Entry entry); + using Hash for DataStructures.L1ToL2Msg; IRegistry immutable REGISTRY; @@ -37,20 +38,7 @@ contract Inbox is IInbox { * @return The hash of the message (used as the key of the entry in the set) */ function computeEntryKey(DataStructures.L1ToL2Msg memory _message) public pure returns (bytes32) { - return bytes32( - uint256( - sha256( - abi.encode( - _message.sender, - _message.recipient, - _message.content, - _message.secretHash, - _message.deadline, - _message.fee - ) - ) - ) % Constants.P - ); + return _message.sha256ToField(); } /** @@ -58,7 +46,7 @@ contract Inbox is IInbox { * @dev Will emit `MessageAdded` with data for easy access by the sequencer * @dev msg.value - The fee provided to sequencer for including the entry * @param _recipient - The recipient of the entry - * @param _deadline - The deadline to consume a message. Only after it, can a message be cancalled. + * @param _deadline - The deadline to consume a message. Only after it, can a message be cancelled. * it is uint32 to for slot packing of the Entry struct. Should work until Feb 2106. * @param _content - The content of the entry (application specific) * @param _secretHash - The secret hash of the entry (make it possible to hide when a specific entry is consumed on L2) diff --git a/l1-contracts/src/core/messagebridge/Outbox.sol b/l1-contracts/src/core/messagebridge/Outbox.sol index 4b1d800a7a65..b590adf0fbf6 100644 --- a/l1-contracts/src/core/messagebridge/Outbox.sol +++ b/l1-contracts/src/core/messagebridge/Outbox.sol @@ -3,8 +3,8 @@ pragma solidity >=0.8.18; import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol"; -import {Constants} from "@aztec/core/libraries/Constants.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; +import {Hash} from "@aztec/core/libraries/Hash.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {MessageBox} from "@aztec/core/libraries/MessageBox.sol"; import {IRegistry} from "@aztec/core/interfaces/messagebridge/IRegistry.sol"; @@ -17,6 +17,7 @@ import {IRegistry} from "@aztec/core/interfaces/messagebridge/IRegistry.sol"; */ contract Outbox is IOutbox { using MessageBox for mapping(bytes32 entryKey => DataStructures.Entry entry); + using Hash for DataStructures.L2ToL1Msg; IRegistry immutable REGISTRY; @@ -37,11 +38,7 @@ contract Outbox is IOutbox { * @return The key of the entry in the set */ function computeEntryKey(DataStructures.L2ToL1Msg memory _message) public pure returns (bytes32) { - // TODO: Replace mod P later on when we have a better idea of how to handle Fields. - return bytes32( - uint256(sha256(abi.encode(_message.sender, _message.recipient, _message.content))) - % Constants.P - ); + return _message.sha256ToField(); } /** diff --git a/l1-contracts/test/portals/RollupNativeAsset.sol b/l1-contracts/test/portals/RollupNativeAsset.sol index e86600ca08db..5b7d4217a0d0 100644 --- a/l1-contracts/test/portals/RollupNativeAsset.sol +++ b/l1-contracts/test/portals/RollupNativeAsset.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.18; import {ERC20} from "@oz/token/ERC20/ERC20.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; -import {Constants} from "@aztec/core/libraries/Constants.sol"; +import {Hash} from "@aztec/core/libraries/Hash.sol"; import {Registry} from "@aztec/core/messagebridge/Registry.sol"; contract RollupNativeAsset is ERC20 { @@ -19,14 +19,12 @@ contract RollupNativeAsset is ERC20 { } function withdraw(uint256 _amount, address _recipient) external returns (bytes32) { - bytes memory contentBytes = - abi.encodeWithSignature("withdraw(uint256,address)", _amount, _recipient); - bytes32 content = bytes32(uint256(sha256(contentBytes)) % Constants.P); - DataStructures.L2ToL1Msg memory message = DataStructures.L2ToL1Msg({ sender: DataStructures.L2Actor(aztecAddress, 1), recipient: DataStructures.L1Actor(address(this), block.chainid), - content: content + content: Hash.sha256ToField( + abi.encodeWithSignature("withdraw(uint256,address)", _amount, _recipient) + ) }); bytes32 entryKey = registry.getOutbox().consume(message); diff --git a/l1-contracts/test/portals/TokenPortal.sol b/l1-contracts/test/portals/TokenPortal.sol index 187b56da38bc..11d43a21c71e 100644 --- a/l1-contracts/test/portals/TokenPortal.sol +++ b/l1-contracts/test/portals/TokenPortal.sol @@ -7,7 +7,7 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; import {IRegistry} from "@aztec/core/interfaces/messagebridge/IRegistry.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; -import {Constants} from "@aztec/core/libraries/Constants.sol"; +import {Hash} from "@aztec/core/libraries/Hash.sol"; contract TokenPortal { using SafeERC20 for IERC20; @@ -40,8 +40,8 @@ contract TokenPortal { DataStructures.L2Actor memory actor = DataStructures.L2Actor(l2TokenAddress, 1); // Hash the message content to be reconstructed in the receiving contract - bytes memory content = abi.encodeWithSignature("mint(uint256,bytes32)", _amount, _to); - bytes32 contentHash = bytes32(uint256(sha256(content)) % Constants.P); + bytes32 contentHash = + Hash.sha256ToField(abi.encodeWithSignature("mint(uint256,bytes32)", _amount, _to)); // Hold the tokens in the portal underlying.safeTransferFrom(msg.sender, address(this), _amount); diff --git a/l1-contracts/test/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index 6852bc20e8f1..620e6d9802a8 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -8,7 +8,7 @@ import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; import {Registry} from "@aztec/core/messagebridge/Registry.sol"; import {Outbox} from "@aztec/core/messagebridge/Outbox.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; -import {Constants} from "@aztec/core/libraries/Constants.sol"; +import {Hash} from "@aztec/core/libraries/Hash.sol"; // Interfaces import {IRegistry} from "@aztec/core/interfaces/messagebridge/IRegistry.sol"; @@ -71,9 +71,7 @@ contract TokenPortalTest is Test { DataStructures.L1ToL2Msg memory expectedMessage = DataStructures.L1ToL2Msg({ sender: DataStructures.L1Actor(address(tokenPortal), 1), recipient: DataStructures.L2Actor(l2TokenAddress, 1), - content: bytes32( - uint256(sha256(abi.encodeWithSignature("mint(uint256,bytes32)", amount, to))) % Constants.P - ), + content: Hash.sha256ToField(abi.encodeWithSignature("mint(uint256,bytes32)", amount, to)), secretHash: secretHash, deadline: deadline, fee: bid