Skip to content

Commit 00d6d36

Browse files
shotaroshotaro
authored andcommitted
implemented safe bridge receiver/sender logic for unhappy path
1 parent cddc2b7 commit 00d6d36

File tree

3 files changed

+131
-92
lines changed

3 files changed

+131
-92
lines changed

contracts/src/bridge/FastBridgeReceiver.sol

Lines changed: 114 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22

33
/**
4-
* @authors: [@shalzz*, @hrishibhat]
4+
* @authors: [@shalzz*, @hrishibhat*, @shotaronowhere]
55
* @reviewers: []
66
* @auditors: []
77
* @bounties: []
@@ -25,9 +25,9 @@ contract FastBridgeReceiver is IFastBridgeReceiver {
2525

2626
struct Claim {
2727
address bridger;
28+
bytes32 messageHash;
2829
uint256 claimedAt;
2930
uint256 claimDeposit;
30-
bool relayed;
3131
bool honest;
3232
}
3333

@@ -38,12 +38,13 @@ contract FastBridgeReceiver is IFastBridgeReceiver {
3838
bool honest;
3939
}
4040

41-
// messageHash => Claim
42-
mapping(bytes32 => Claim) public claims;
43-
mapping(bytes32 => Challenge) public challenges;
41+
// fastMessageIndex => Claim[]
42+
mapping(uint256 => Claim[]) public claims;
43+
mapping(uint256 => Challenge[]) public challenges;
44+
mapping(uint256 => bool) public relayed;
4445

45-
event ClaimReceived(bytes32 messageHash, uint256 claimedAt);
46-
event ClaimChallenged(bytes32 messageHash, uint256 challengedAt);
46+
event ClaimReceived(uint256 _fastMessageIndex, bytes32 _messageHash, uint256 claimedAt);
47+
event ClaimChallenged(uint256 _fastMessageIndex, uint256 _claimIndex, bytes32 _messageHash, uint256 challengedAt);
4748

4849
modifier onlyByGovernor() {
4950
require(governor == msg.sender, "Access not allowed: Governor only.");
@@ -66,136 +67,170 @@ contract FastBridgeReceiver is IFastBridgeReceiver {
6667
challengeDuration = _challengeDuration;
6768
}
6869

69-
function claim(bytes32 _messageHash) external payable {
70+
function claim(uint256 _fastMessageIndex, bytes32 _messageHash) external payable override {
7071
require(msg.value >= claimDeposit, "Not enough claim deposit");
71-
require(claims[_messageHash].bridger == address(0), "Claimed already made");
72+
require(relayed[_fastMessageIndex] == false, "Message already relayed.");
73+
// only accept additional claims if the previous claims are successfully challenged
74+
// AND the message is not yet relayed
75+
require(claims[_fastMessageIndex].length == 0 || challenges[_fastMessageIndex][claims[_fastMessageIndex].length-1].honest, "There is a previous unresolved claim");
7276

73-
claims[_messageHash] = Claim({
77+
claims[_fastMessageIndex].push( Claim({
7478
bridger: msg.sender,
79+
messageHash: _messageHash,
7580
claimedAt: block.timestamp,
7681
claimDeposit: msg.value,
77-
honest: false,
78-
relayed: false
79-
});
82+
honest: false
83+
}));
8084

81-
emit ClaimReceived(_messageHash, block.timestamp);
85+
emit ClaimReceived(_fastMessageIndex, _messageHash, block.timestamp);
8286
}
8387

84-
function verifyAndRelay(bytes32 _messageHash, bytes memory _encodedData) external {
88+
function verifyAndRelay(uint _fastMessageIndex, bytes memory _encodedData) external override{
89+
90+
Claim[] storage claimsForIndex = claims[_fastMessageIndex];
91+
require(claimsForIndex.length>0, "Claim does not exist");
92+
8593

86-
Claim storage claim = claims[_messageHash];
87-
require(claim.bridger != address(0), "Claim does not exist");
88-
require(claim.claimedAt + challengeDuration < block.timestamp, "Challenge period not over");
89-
require(claim.relayed == false, "Message already relayed");
94+
require(claimsForIndex[claimsForIndex.length-1].claimedAt + challengeDuration < block.timestamp, "Challenge period not over");
95+
require(relayed[_fastMessageIndex] == false, "Message already relayed");
9096

91-
Challenge storage challenge = challenges[_messageHash];
92-
require(challenge.challenger == address(0), "This claim is Challenged");
97+
Challenge[] storage challengesForIndex = challenges[_fastMessageIndex];
9398

94-
if(keccak256(_encodedData) == _messageHash){
95-
claim.honest = true;
99+
require(challengesForIndex.length<claimsForIndex.length, "This claim is Challenged");
100+
101+
if(keccak256(_encodedData) == claimsForIndex[claimsForIndex.length-1].messageHash){
102+
claimsForIndex[claimsForIndex.length-1].honest = true;
96103
// Decode the receiver address from the data encoded by the IFastBridgeSender
97104
(address receiver, bytes memory data) = abi.decode(_encodedData, (address, bytes));
98105
(bool success, ) = address(receiver).call(data);
99106
require(success, "Failed to call contract");
100107

101-
claim.relayed = true;
108+
relayed[_fastMessageIndex] = true;
102109
}
103110

104111
}
105112

106-
function relayRule(bytes memory _encodedData) external {
113+
function relayRule(bytes memory _encodedData) external override{
107114
IBridge bridge = inbox.bridge();
108115
require(msg.sender == address(bridge), "Not called by the Bridge");
109116

110117
IOutbox outbox = IOutbox(inbox.bridge().activeOutbox());
111118
address l2Sender = outbox.l2ToL1Sender();
112119
require(l2Sender == safebridge, "Can be relayed only by Safe Bridge");
113120

114-
bytes32 _messageHash = keccak256(_encodedData);
115-
Challenge storage challenge = challenges[_messageHash];
116-
Claim storage claim = claims[_messageHash];
117-
require(claim.relayed != true, "Claim already relayed");
118-
119-
if (claim.bridger != address(0) && challenge.challenger != address(0)) {
120-
challenge.honest = false;
121-
claim.honest = true;
122-
} else {
123-
challenge.honest = true;
124-
claim.honest = false;
125-
}
121+
(uint256 _fastMessageIndex, bytes memory dataWithReceiver) = abi.decode(_encodedData, (uint256, bytes));
122+
bytes32 messageHash = keccak256(dataWithReceiver);
126123

127-
// Decode the receiver address from the data encoded by the IFastBridgeSender
128-
(address receiver, bytes memory data) = abi.decode(_encodedData, (address, bytes));
129-
(bool success, ) = address(receiver).call(data);
130-
require(success, "Failed to call contract");
124+
require(relayed[_fastMessageIndex] != true, "Claim already relayed");
131125

132-
claim.relayed = true;
126+
Claim[] storage claimsForIndex = claims[_fastMessageIndex];
127+
Challenge[] storage challengesForIndex = challenges[_fastMessageIndex];
128+
129+
if (claimsForIndex.length > 0){
130+
// has claims
131+
if (claimsForIndex[claimsForIndex.length-1].messageHash == messageHash){
132+
claimsForIndex[claimsForIndex.length-1].honest = true;
133+
} else if(challengesForIndex.length > 0){
134+
// has challenges
135+
challengesForIndex[challengesForIndex.length - 1].honest = true;
136+
}
137+
} else{
138+
// no claims, no challenges
139+
}
140+
// dispute ruled
141+
if (dataWithReceiver.length != 0){
142+
// Decode the receiver address from the data encoded by the IFastBridgeSender
143+
(address receiver, bytes memory data) = abi.decode(dataWithReceiver, (address, bytes));
144+
(bool success, ) = address(receiver).call(data);
145+
require(success, "Failed to call contract");
146+
147+
relayed[_fastMessageIndex] = true;
148+
}
149+
133150

134151
}
135152

136-
function withdrawClaimDeposit(bytes32 _messageHash) external {
137-
Claim storage claim = claims[_messageHash];
138-
require(claim.bridger != address(0), "Claim does not exist");
139-
require(claim.claimedAt + challengeDuration < block.timestamp, "Challenge period not over");
140-
require(claim.relayed == true, "Claim not relayed");
141-
142-
Challenge storage challenge = challenges[_messageHash];
143-
uint256 amount = claim.claimDeposit;
144-
if(challenge.honest == false){
145-
amount += challenge.challengeDeposit;
146-
challenge.challengeDeposit = 0;
153+
function withdrawClaimDeposit(uint256 _fastMessageIndex) external override{
154+
Claim[] storage claimsForIndex = claims[_fastMessageIndex];
155+
require(claimsForIndex.length>0, "Claim does not exist");
156+
157+
158+
require(claimsForIndex[claimsForIndex.length-1].claimedAt + challengeDuration < block.timestamp, "Challenge period not over");
159+
require(relayed[_fastMessageIndex] == true, "Claim not relayed");
160+
161+
Challenge[] storage challengesForIndex = challenges[_fastMessageIndex];
162+
163+
uint256 amount = 0;
164+
if(claimsForIndex[claimsForIndex.length-1].honest == true){
165+
amount += claimsForIndex[claimsForIndex.length-1].claimDeposit;
166+
if(challengesForIndex.length == claimsForIndex.length){
167+
if(challengesForIndex[challengesForIndex.length - 1].honest == false){
168+
amount += challengesForIndex[challengesForIndex.length - 1].challengeDeposit;
169+
challengesForIndex[challengesForIndex.length - 1].challengeDeposit = 0;
170+
}
171+
}
147172
}
148173

149-
claim.claimDeposit = 0;
150-
payable(claim.bridger).send(amount);
174+
claimsForIndex[claimsForIndex.length-1].claimDeposit = 0;
175+
payable(claimsForIndex[claimsForIndex.length-1].bridger).send(amount);
151176
}
152177

153-
function challenge(bytes32 _messageHash) external payable {
154-
Claim memory claim = claims[_messageHash];
155-
require(claim.bridger != address(0), "Claim does not exist");
156-
require(block.timestamp - claim.claimedAt < challengeDuration, "Challenge period over");
178+
function challenge(uint256 _fastMessageIndex) external payable {
179+
Claim[] memory claimsForIndex = claims[_fastMessageIndex];
180+
require(claimsForIndex.length > 0, "Claim does not exist");
181+
require(relayed[_fastMessageIndex] == false, "Message already relayed");
182+
require(block.timestamp - claimsForIndex[claimsForIndex.length-1].claimedAt < challengeDuration, "Challenge period over");
157183
require(msg.value >= challengeDeposit, "Not enough challenge deposit");
158-
require(challenges[_messageHash].challenger == address(0), "Claim already challenged");
184+
require(challenges[_fastMessageIndex].length < claimsForIndex.length, "Claim already challenged");
159185

160-
challenges[_messageHash] = Challenge({
186+
challenges[_fastMessageIndex].push(Challenge({
161187
challenger: msg.sender,
162188
challengedAt: block.timestamp,
163189
challengeDeposit: msg.value,
164190
honest: false
165-
});
191+
}));
166192

167-
emit ClaimChallenged(_messageHash, block.timestamp);
193+
emit ClaimChallenged(_fastMessageIndex,claimsForIndex.length-1, claimsForIndex[claimsForIndex.length-1].messageHash, block.timestamp);
168194
}
169195

170-
function withdrawChallengeDeposit(bytes32 _messageHash) external {
171-
Challenge storage challenge = challenges[_messageHash];
172-
require(challenge.challenger != address(0), "Challenge does not exist");
173-
require(challenge.honest == true, "Challenge not honest");
196+
function withdrawChallengeDeposit(uint256 _fastMessageIndex, uint256 challengeIndex) external {
197+
Challenge[] storage challengesForIndex = challenges[_fastMessageIndex];
198+
require(challengesForIndex.length>0 && challengeIndex < challengesForIndex.length, "Challenge does not exist");
174199

175-
Claim storage claim = claims[_messageHash];
176-
require(block.timestamp > claim.claimedAt + challengeDuration, "Challenge period not over");
177-
require(claim.relayed == true, "Claim not relayed");
200+
Claim[] storage claimsForIndex = claims[_fastMessageIndex];
178201

179-
uint256 amount = challenge.challengeDeposit;
180-
if(claim.honest == false){
181-
amount += claim.claimDeposit;
182-
claim.claimDeposit = 0;
202+
uint256 amount = 0;
203+
if(challengesForIndex[challengeIndex].honest == true){
204+
amount += challengesForIndex[challengeIndex].challengeDeposit;
205+
if(claimsForIndex[challengeIndex].honest == false){
206+
amount += claimsForIndex[challengeIndex].claimDeposit;
207+
claimsForIndex[challengeIndex].claimDeposit = 0;
208+
}
183209
}
184210

185-
challenge.challengeDeposit = 0;
186-
payable(challenge.challenger).send(amount);
211+
212+
challengesForIndex[challengeIndex].challengeDeposit = 0;
213+
payable(challengesForIndex[challengeIndex].challenger).send(amount);
187214
}
188215
//**** View Functions ****//
189-
function challengePeriod(bytes32 _messageHash) public view returns(uint start, uint end) {
190-
start = claims[_messageHash].claimedAt;
191-
end = start + challengeDuration;
216+
function challengePeriod(uint256 _fastMessageIndex) public view returns(uint start, uint end) {
217+
if (claims[_fastMessageIndex].length > 0){
218+
start = claims[_fastMessageIndex][claims[_fastMessageIndex].length-1].claimedAt;
219+
end = start + challengeDuration;
220+
}
221+
192222
return (start, end);
193223
}
224+
194225
//**** Governor functions ****//
195226

196227
function setClaimDeposit(uint256 _claimDeposit) external onlyByGovernor {
197228
claimDeposit = _claimDeposit;
198229
}
230+
//TODO
231+
function viewClaimDeposit() external override view returns (uint256 amount){
232+
return 0;
233+
}
199234

200235
function setChallengePeriodDuration(uint256 _challengeDuration) external onlyByGovernor {
201236
challengeDuration = _challengeDuration;

contracts/src/bridge/FastBridgeSender.sol

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22

33
/**
4-
* @authors: [@shalzz*, @hrishibhat]
4+
* @authors: [@shalzz*, @hrishibhat*, @shotaronowhere]
55
* @reviewers: []
66
* @auditors: []
77
* @bounties: []
@@ -18,12 +18,13 @@ contract FastBridgeSender is IFastBridgeSender {
1818
ISafeBridge public safebridge;
1919
IFastBridgeReceiver public fastBridgeReceiver;
2020
address public fastSender;
21-
21+
mapping(uint256 => bytes) public fastMessages;
22+
uint256 fastMessageIndex;
2223
/**
2324
* The bridgers need to watch for these events and
2425
* relay the messageHash on the FastBridgeReceiver.
2526
*/
26-
event OutgoingMessage(address target, bytes32 messageHash, bytes message);
27+
event OutgoingMessage(address target, bytes32 messageHash, uint256 fastMessageIndex, bytes message);
2728

2829
constructor(ISafeBridge _safebridge, IFastBridgeReceiver _fastBridgeReceiver) {
2930
safebridge = _safebridge;
@@ -42,13 +43,15 @@ contract FastBridgeSender is IFastBridgeSender {
4243
* @param _receiver The L1 contract address who will receive the calldata
4344
* @param _calldata The receiving domain encoded message data.
4445
*/
45-
function sendFast(address _receiver, bytes memory _calldata) external {
46+
function sendFast(address _receiver, bytes memory _calldata) external override {
4647
require(msg.sender == fastSender, "Access not allowed: Fast Sender only.");
4748

4849
// Encode the receiver address with the function signature + arguments i.e calldata
4950
bytes memory encodedData = abi.encode(_receiver, _calldata);
51+
fastMessages[fastMessageIndex] = encodedData;
52+
fastMessageIndex += 1;
5053

51-
emit OutgoingMessage(_receiver, keccak256(encodedData), encodedData);
54+
emit OutgoingMessage(_receiver, keccak256(encodedData), fastMessageIndex-1, encodedData);
5255
}
5356

5457
/**
@@ -60,10 +63,10 @@ contract FastBridgeSender is IFastBridgeSender {
6063
* It may require some ETH (or whichever native token) to pay for the bridging cost,
6164
* depending on the underlying safe bridge.
6265
*
63-
* @param _receiver The L1 contract address who will receive the calldata
64-
* @param _calldata The receiving domain encoded message data.
66+
* @param _fastMessageIndex The index of messageHash to send
6567
*/
66-
function sendSafe(address _receiver, bytes memory _calldata) external payable {
68+
function sendSafe(uint256 _fastMessageIndex) external payable {
69+
6770
// The safe bridge sends the encoded data to the FastBridgeReceiver
6871
// in order for the FastBridgeReceiver to resolve any potential
6972
// challenges and then forwards the message to the actual
@@ -72,8 +75,9 @@ contract FastBridgeSender is IFastBridgeSender {
7275
// IFastBridgeReceiver function.
7376
// TODO: add access checks for this on the FastBridgeReceiver.
7477
// TODO: how much ETH should be provided for bridging? add an ISafeBridge.bridgingCost()
78+
require(_fastMessageIndex<fastMessageIndex, "Fast message does not exit.");
7579

76-
bytes memory encodedData = abi.encode(_receiver, _calldata);
80+
bytes memory encodedData = abi.encode(_fastMessageIndex, fastMessages[_fastMessageIndex]);
7781
bytes memory encodedTxData = abi.encodeWithSelector(fastBridgeReceiver.relayRule.selector, encodedData);
7882
safebridge.sendSafe{value: msg.value}(address(fastBridgeReceiver), encodedTxData);
7983
}

contracts/src/bridge/interfaces/IFastBridgeReceiver.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
pragma solidity ^0.8.0;
44

55
interface IFastBridgeReceiver {
6-
function claim(bytes32 _messageHash) external payable;
6+
function claim(uint256 _fastMessageIndex, bytes32 _messageHash) external payable;
77

8-
function verifyAndRelay(bytes32 _messageHash, bytes memory _calldata) external;
8+
function verifyAndRelay(uint _fastMessageIndex, bytes memory _encodedData) external;
99

10-
function relayRule(bytes memory _calldata) external;
10+
function relayRule(bytes memory _encodedData) external;
1111

12-
function withdrawClaimDeposit(bytes32 _messageHash) external;
12+
function withdrawClaimDeposit(uint256 _fastMessageIndex) external;
1313

1414
function viewClaimDeposit() external view returns (uint256 amount);
1515
}

0 commit comments

Comments
 (0)