The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
hyperlane-monorepo/solidity/contracts/isms/hook/AbstractMessageIdAuthorized...

117 lines
4.0 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {LibBit} from "../../libs/LibBit.sol";
import {Message} from "../../libs/Message.sol";
// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/**
* @title AbstractMessageIdAuthorizedIsm
* @notice Uses external verification options to verify interchain messages which need a authorized caller
*/
abstract contract AbstractMessageIdAuthorizedIsm is
IInterchainSecurityModule,
Initializable
{
using Address for address payable;
using LibBit for uint256;
using Message for bytes;
// ============ Public Storage ============
/// @notice Maps messageId to whether or not the message has been verified
/// first bit is boolean for verification
/// rest of bits is the amount to send to the recipient
/// @dev bc of the bit packing, we can only send up to 2^255 wei
/// @dev the first bit is reserved for verification and the rest 255 bits are for the msg.value
mapping(bytes32 => uint256) public verifiedMessages;
/// @notice Index of verification bit in verifiedMessages
uint256 public constant VERIFIED_MASK_INDEX = 255;
/// @notice address for the authorized hook
bytes32 public authorizedHook;
// ============ Events ============
/// @notice Emitted when a message is received from the external bridge
event ReceivedMessage(bytes32 indexed messageId);
// ============ Initializer ============
function setAuthorizedHook(bytes32 _hook) external initializer {
require(
_hook != bytes32(0),
"AbstractMessageIdAuthorizedIsm: invalid authorized hook"
);
authorizedHook = _hook;
}
// ============ External Functions ============
/**
* @notice Verify a message was received by ISM.
* @param message Message to verify.
*/
function verify(
bytes calldata,
/*_metadata*/
bytes calldata message
) external returns (bool) {
bytes32 messageId = message.id();
// check for the first bit (used for verification)
bool verified = verifiedMessages[messageId].isBitSet(
VERIFIED_MASK_INDEX
);
// rest 255 bits contains the msg.value passed from the hook
if (verified) {
uint256 _msgValue = verifiedMessages[messageId].clearBit(
VERIFIED_MASK_INDEX
);
if (_msgValue > 0) {
verifiedMessages[messageId] -= _msgValue;
payable(message.recipientAddress()).sendValue(_msgValue);
}
}
return verified;
}
/**
* @notice Receive a message from the AbstractMessageIdAuthHook
* @dev Only callable by the authorized hook.
* @param messageId Hyperlane Id of the message.
*/
function verifyMessageId(bytes32 messageId) public payable virtual {
require(
_isAuthorized(),
"AbstractMessageIdAuthorizedIsm: sender is not the hook"
);
require(
msg.value < 2 ** VERIFIED_MASK_INDEX,
"AbstractMessageIdAuthorizedIsm: msg.value must be less than 2^255"
);
verifiedMessages[messageId] = msg.value.setBit(VERIFIED_MASK_INDEX);
emit ReceivedMessage(messageId);
}
function _isAuthorized() internal view virtual returns (bool);
}