Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store hash of execution data #103

Merged
merged 4 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
126 changes: 90 additions & 36 deletions src/MultiBridgeMessageReceiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import "./libraries/Message.sol";
/// governance timelock contract.
/// @dev The contract only accepts messages from trusted bridge receiver adapters, each of which implements the
/// IMessageReceiverAdapter interface.
ermyas marked this conversation as resolved.
Show resolved Hide resolved
contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAware {
contract MultiBridgeMessageReceiver is
IMultiBridgeMessageReceiver,
ExecutorAware
{
/// @notice the id of the source chain that this contract can receive messages from
uint256 public immutable srcChainId;
/// @notice the global access control contract
Expand All @@ -37,13 +40,14 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
address public governanceTimelock;

/// @notice maintains which bridge adapters have delivered each message
mapping(bytes32 msgId => mapping(address receiverAdapter => bool delivered)) public msgDeliveries;
mapping(bytes32 msgId => mapping(address receiverAdapter => bool delivered))
public msgDeliveries;

/// @notice count of bridge adapters that have delivered each message
mapping(bytes32 msgId => uint256 deliveryCount) public msgDeliveryCount;

/// @notice the data required for executing a message
mapping(bytes32 msgId => ExecutionData execData) public msgExecData;
/// @notice the hash of the params required for executing a message
mapping(bytes32 msgId => bytes32 execParamsHash) public msgExecParamsHash;

/// @notice whether a message has been sent to the governance timelock for execution
mapping(bytes32 msgId => bool scheduled) public isExecutionScheduled;
Expand Down Expand Up @@ -73,7 +77,12 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
////////////////////////////////////////////////////////////////*/

/// @notice sets the initial parameters
constructor(uint256 _srcChainId, address _gac, address[] memory _receiverAdapters, uint64 _quorum) {
constructor(
uint256 _srcChainId,
address _gac,
address[] memory _receiverAdapters,
uint64 _quorum
) {
if (_srcChainId == 0) {
revert Error.INVALID_SENDER_CHAIN_ID();
}
Expand All @@ -84,7 +93,7 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
srcChainId = _srcChainId;
gac = IGAC(_gac);

for (uint256 i; i < _receiverAdapters.length;) {
for (uint256 i; i < _receiverAdapters.length; ) {
_updateReceiverAdapter(_receiverAdapters[i], true);
unchecked {
++i;
Expand All @@ -99,7 +108,9 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar

/// @notice receive messages from allowed bridge receiver adapters
/// @param _message is the crosschain message sent by the mma sender
function receiveMessage(MessageLibrary.Message calldata _message) external override onlyReceiverAdapter {
function receiveMessage(
MessageLibrary.Message calldata _message
) external override onlyReceiverAdapter {
if (_message.dstChainId != block.chainid) {
revert Error.INVALID_DST_CHAIN();
}
Expand Down Expand Up @@ -127,29 +138,42 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar

msgDeliveries[msgId][msg.sender] = true;

/// increment quorum
/// increment vote count for a message
++msgDeliveryCount[msgId];

/// stores the execution data required
ExecutionData memory prevStored = msgExecData[msgId];
/// stores the hash of the execution params required
bytes32 prevStoredHash = msgExecParamsHash[msgId];

/// stores the message if the amb is the first one delivering the message
if (prevStored.target == address(0)) {
msgExecData[msgId] = ExecutionData(
_message.target, _message.callData, _message.nativeValue, _message.nonce, _message.expiration
);
if (prevStoredHash == bytes32(0)) {
msgExecParamsHash[msgId] = MessageLibrary
.computeExecutionParamsHash(_message);
}

string memory bridgeName = IMessageReceiverAdapter(msg.sender).name();
emit BridgeMessageReceived(msgId, bridgeName, _message.nonce, msg.sender);
emit BridgeMessageReceived(
msgId,
bridgeName,
_message.nonce,
msg.sender
);
}

/// @inheritdoc IMultiBridgeMessageReceiver
function scheduleMessageExecution(bytes32 _msgId) external override {
ExecutionData memory _execData = msgExecData[_msgId];
function scheduleMessageExecution(
bytes32 _msgId,
MessageLibrary.MessageExecutionParams calldata _execParams
) external override {
bytes32 execParamsHash = msgExecParamsHash[_msgId];
if (
MessageLibrary.computeExecutionParamsHash(_execParams) !=
execParamsHash
) {
revert Error.EXEC_PARAMS_HASH_MISMATCH();
}

/// @dev validates if msg execution is not beyond expiration
if (block.timestamp > _execData.expiration) {
if (block.timestamp > _execParams.expiration) {
revert Error.MSG_EXECUTION_PASSED_DEADLINE();
}

Expand All @@ -167,30 +191,42 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar

/// @dev queues the action on timelock for execution
IGovernanceTimelock(governanceTimelock).scheduleTransaction(
_execData.target, _execData.value, _execData.callData
_execParams.target,
_execParams.value,
_execParams.callData
);

emit MessageExecutionScheduled(_msgId, _execData.target, _execData.value, _execData.nonce, _execData.callData);
emit MessageExecutionScheduled(
_msgId,
_execParams.target,
_execParams.value,
_execParams.nonce,
_execParams.callData
);
}

/// @notice update the governance timelock contract.
/// @dev called by admin to update the timelock contract
function updateGovernanceTimelock(address _governanceTimelock) external onlyGlobalOwner {
function updateGovernanceTimelock(
address _governanceTimelock
) external onlyGlobalOwner {
if (_governanceTimelock == address(0)) {
revert Error.ZERO_GOVERNANCE_TIMELOCK();
}
address oldGovernanceTimelock = governanceTimelock;
governanceTimelock = _governanceTimelock;
emit GovernanceTimelockUpdated(oldGovernanceTimelock, _governanceTimelock);
emit GovernanceTimelockUpdated(
oldGovernanceTimelock,
_governanceTimelock
);
}

/// @notice Update bridge receiver adapters.
/// @dev called by admin to update receiver bridge adapters on all other chains
function updateReceiverAdapters(address[] calldata _receiverAdapters, bool[] calldata _operations)
external
override
onlyGlobalOwner
{
function updateReceiverAdapters(
address[] calldata _receiverAdapters,
bool[] calldata _operations
) external override onlyGlobalOwner {
_updateReceiverAdapters(_receiverAdapters, _operations);
_validateQuorum(quorum);
}
Expand Down Expand Up @@ -219,16 +255,20 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
/// @return isExecutionScheduled is true if the message has been sent to the timelock for execution
/// @return msgCurrentVotes is the number of bridges that have delivered the message
/// @return successfulBridge is the list of bridges that have delivered the message
function getMessageInfo(bytes32 _msgId) public view returns (bool, uint256, string[] memory) {
function getMessageInfo(
bytes32 _msgId
) public view returns (bool, uint256, string[] memory) {
uint256 msgCurrentVotes = msgDeliveryCount[_msgId];
string[] memory successfulBridge = new string[](msgCurrentVotes);

if (msgCurrentVotes != 0) {
uint256 currIndex;
address[] memory executors = getTrustedExecutors();
for (uint256 i; i < executors.length;) {
for (uint256 i; i < executors.length; ) {
if (msgDeliveries[_msgId][executors[i]]) {
successfulBridge[currIndex] = IMessageReceiverAdapter(executors[i]).name();
successfulBridge[currIndex] = IMessageReceiverAdapter(
executors[i]
).name();
++currIndex;
}

Expand All @@ -238,7 +278,11 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
}
}

return (isExecutionScheduled[_msgId], msgCurrentVotes, successfulBridge);
return (
isExecutionScheduled[_msgId],
msgCurrentVotes,
successfulBridge
);
}

/*/////////////////////////////////////////////////////////////////
Expand All @@ -254,7 +298,10 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
emit QuorumUpdated(oldValue, _quorum);
}

function _updateReceiverAdapters(address[] memory _receiverAdapters, bool[] memory _operations) private {
function _updateReceiverAdapters(
address[] memory _receiverAdapters,
bool[] memory _operations
) private {
uint256 len = _receiverAdapters.length;

if (len == 0) {
Expand All @@ -265,7 +312,7 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
revert Error.ARRAY_LENGTH_MISMATCHED();
}

for (uint256 i; i < len;) {
for (uint256 i; i < len; ) {
_updateReceiverAdapter(_receiverAdapters[i], _operations[i]);

unchecked {
Expand All @@ -274,15 +321,22 @@ contract MultiBridgeMessageReceiver is IMultiBridgeMessageReceiver, ExecutorAwar
}
}

function _updateReceiverAdapter(address _receiverAdapter, bool _add) private {
function _updateReceiverAdapter(
address _receiverAdapter,
bool _add
) private {
if (_receiverAdapter == address(0)) {
revert Error.ZERO_ADDRESS_INPUT();
}
bool success = _add ? _addTrustedExecutor(_receiverAdapter) : _removeTrustedExecutor(_receiverAdapter);
bool success = _add
? _addTrustedExecutor(_receiverAdapter)
: _removeTrustedExecutor(_receiverAdapter);

if (!success) {
// only fails because we are either attempting to add an existing adapter, or remove a non-existing adapter
revert Error.UPDATE_RECEIVER_ADAPTER_FAILED(_add ? "adapter already added" : "adapter not found");
revert Error.UPDATE_RECEIVER_ADAPTER_FAILED(
_add ? "adapter already added" : "adapter not found"
);
}

emit BridgeReceiverAdapterUpdated(_receiverAdapter, _add);
Expand Down
41 changes: 22 additions & 19 deletions src/interfaces/IMultiBridgeMessageReceiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,16 @@ import "../libraries/Message.sol";

/// @notice interface for the multi-bridge message receiver
interface IMultiBridgeMessageReceiver {
/// @notice encapsulates data that is relevant to a message's intended transaction execution.
struct ExecutionData {
// target contract address on the destination chain
address target;
// data to pass to target by low-level call
bytes callData;
// value to pass to target by low-level call
uint256 value;
// nonce of the message
uint256 nonce;
// expiration timestamp for the message beyond which it cannot be executed
uint256 expiration;
}

/// @notice emitted when a message has been received from a single bridge.
/// @param msgId is the unique identifier of the message
/// @param bridgeName is the name of the bridge from which the message was received
/// @param nonce is the nonce of the message
/// @param receiverAdapter is the address of the receiver adapter that received the message
event BridgeMessageReceived(
bytes32 indexed msgId, string indexed bridgeName, uint256 nonce, address receiverAdapter
bytes32 indexed msgId,
string indexed bridgeName,
uint256 nonce,
address receiverAdapter
);

/// @notice emitted when a message has been queued for execution in the destination timelock contract.
Expand All @@ -35,13 +24,20 @@ interface IMultiBridgeMessageReceiver {
/// @param nonce is the nonce of the message
/// @param callData is the data that will be passed to the target address through low-level call
event MessageExecutionScheduled(
bytes32 indexed msgId, address indexed target, uint256 nativeValue, uint256 nonce, bytes callData
bytes32 indexed msgId,
address indexed target,
uint256 nativeValue,
uint256 nonce,
bytes callData
);

/// @notice emitted when receiver adapter of a specific bridge is updated.
/// @param receiverAdapter is the new receiver adapter address
/// @param add is true if the receiver adapter was added, false if removed
event BridgeReceiverAdapterUpdated(address indexed receiverAdapter, bool add);
event BridgeReceiverAdapterUpdated(
address indexed receiverAdapter,
bool add
);

/// @notice emitted when the quorum for message validity is updated.
/// @param oldQuorum is the old quorum value
Expand All @@ -60,12 +56,19 @@ interface IMultiBridgeMessageReceiver {

/// @notice Sends a message, that has achieved quorum and has not yet expired, to the governance timelock for eventual execution.
/// @param _msgId is the unique identifier of the message
ermyas marked this conversation as resolved.
Show resolved Hide resolved
function scheduleMessageExecution(bytes32 _msgId) external;
/// @param _execParams are the params for message execution
function scheduleMessageExecution(
bytes32 _msgId,
MessageLibrary.MessageExecutionParams calldata _execParams
) external;

/// @notice adds or removes bridge receiver adapters.
/// @param _receiverAdapters the list of receiver adapters to add or remove
/// @param _operations the list of operations to perform for corresponding receiver adapters, true for add, false for remove
function updateReceiverAdapters(address[] calldata _receiverAdapters, bool[] calldata _operations) external;
function updateReceiverAdapters(
address[] calldata _receiverAdapters,
bool[] calldata _operations
) external;

/// @notice updates the quorum for message validity, which is the number of bridges that must deliver the message for it to be considered valid.
/// @param _quorum is the new quorum value
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Error.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ library Error {
/// @dev is thrown if refund address is zero (or) invalid
error INVALID_REFUND_ADDRESS();

/// @dev is thrown if execution params do not match the stored hash
error EXEC_PARAMS_HASH_MISMATCH();

/*/////////////////////////////////////////////////////////////////
ADAPTER ERRORS
////////////////////////////////////////////////////////////////*/
Expand Down
Loading
Loading