Skip to content

Commit

Permalink
feat(protocol): allow bridge to fail a message by the owner without r…
Browse files Browse the repository at this point in the history
…etrying it (#16669)
  • Loading branch information
dantaik authored Apr 6, 2024
1 parent 66a35e2 commit dce651e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
19 changes: 19 additions & 0 deletions packages/protocol/contracts/bridge/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ contract Bridge is EssentialContract, IBridge {
error B_INVALID_USER();
error B_INVALID_VALUE();
error B_INVOCATION_TOO_EARLY();
error B_MESSAGE_FAILED();
error B_MESSAGE_NOT_PROVEN();
error B_MESSAGE_NOT_SENT();
error B_MESSAGE_NOT_SUSPENDED();
Expand Down Expand Up @@ -371,6 +372,24 @@ contract Bridge is EssentialContract, IBridge {
emit MessageRetried(msgHash);
}

/// @inheritdoc IBridge
function failMessage(Message calldata _message)
external
whenNotPaused
sameChain(_message.destChainId)
nonReentrant
{
if (msg.sender != _message.destOwner) revert B_PERMISSION_DENIED();

bytes32 msgHash = hashMessage(_message);
if (messageStatus[msgHash] != Status.RETRIABLE) {
revert B_NON_RETRIABLE();
}

_updateMessageStatus(msgHash, Status.FAILED);
emit MessageFailed(msgHash);
}

/// @inheritdoc IBridge
function isMessageSent(Message calldata _message) external view returns (bool) {
if (_message.srcChainId != block.chainid) return false;
Expand Down
10 changes: 10 additions & 0 deletions packages/protocol/contracts/bridge/IBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ interface IBridge {
/// @param msgHash The hash of the message.
event MessageRetried(bytes32 indexed msgHash);

/// @notice Emitted when a message is marked failed.
/// @param msgHash The hash of the message.
event MessageFailed(bytes32 indexed msgHash);

/// @notice Emitted when the status of a message changes.
/// @param msgHash The hash of the message.
/// @param status The new status of the message.
Expand Down Expand Up @@ -143,6 +147,12 @@ interface IBridge {
/// message.
function retryMessage(Message calldata _message, bool _isLastAttempt) external;

/// @notice Mark a message as failed if the message is currently retriable.
/// @dev This function can only be called by `message.destOwner`.
/// @param _message The message to fail.
/// message.
function failMessage(Message calldata _message) external;

/// @notice Returns the bridge state context.
/// @return ctx_ The context of the current bridge operation.
function context() external view returns (Context memory ctx_);
Expand Down
24 changes: 24 additions & 0 deletions packages/protocol/test/bridge/Bridge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,30 @@ contract BridgeTest is TaikoTest {
assertEq(postRetryStatus == IBridge.Status.FAILED, true);
}

function test_Bridge_fail_message() public {
vm.startPrank(Alice);
(IBridge.Message memory message, bytes memory proof) =
setUpPredefinedSuccessfulProcessMessageCall();

// etch bad receiver at the to address, so it fails.
vm.etch(message.to, address(badReceiver).code);

bytes32 msgHash = destChainBridge.hashMessage(message);

destChainBridge.processMessage(message, proof);

IBridge.Status status = destChainBridge.messageStatus(msgHash);

assertEq(status == IBridge.Status.RETRIABLE, true);

vm.stopPrank();

vm.prank(message.destOwner);
destChainBridge.failMessage(message);
IBridge.Status postRetryStatus = destChainBridge.messageStatus(msgHash);
assertEq(postRetryStatus == IBridge.Status.FAILED, true);
}

function test_processMessage_InvokeMessageCall_DoS1() public {
nonmaliciousContract1 = new NonMaliciousContract1();

Expand Down

0 comments on commit dce651e

Please sign in to comment.