Skip to content

Commit ffb594f

Browse files
authored
Merge pull request #429 from kleros/fix/message-encoding
Fix/message encoding
2 parents 24b8a82 + 806eac1 commit ffb594f

File tree

16 files changed

+364
-268
lines changed

16 files changed

+364
-268
lines changed

contracts/src/arbitrumToEth/VeaInboxArbToEth.sol

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: MIT
22

3-
/// @custom:authors: [@jaybuidl, @shotaronowhere]
3+
/// @custom:authors: [@jaybuidl, @mani99brar, @shotaronowhere]
44
/// @custom:reviewers: []
55
/// @custom:auditors: []
66
/// @custom:bounties: []
@@ -70,26 +70,16 @@ contract VeaInboxArbToEth is IVeaInbox {
7070
/// Amortized cost is constant.
7171
/// Note: See docs for details how inbox manages merkle tree state.
7272
/// @param _to The address of the contract on the receiving chain which receives the calldata.
73-
/// @param _fnSelector The function selector of the receiving contract.
74-
/// @param _data The message calldata, abi.encode(param1, param2, ...)
73+
/// @param _data The message data for the receiving gateway.
7574
/// @return msgId The zero based index of the message in the inbox.
76-
function sendMessage(address _to, bytes4 _fnSelector, bytes memory _data) external override returns (uint64) {
75+
function sendMessage(address _to, bytes calldata _data) external override returns (uint64) {
7776
uint64 oldCount = count;
7877

7978
// Given arbitrum's speed limit of 7 million gas / second, it would take atleast 8 million years of full blocks to overflow.
8079
// It *should* be impossible to overflow, but we check to be safe when appending to the tree.
8180
require(oldCount < type(uint64).max, "Inbox is full.");
8281

83-
bytes memory nodeData = abi.encodePacked(
84-
oldCount,
85-
_to,
86-
// _data is abi.encode(param1, param2, ...), we need to encode it again to get the correct leaf data
87-
abi.encodePacked( // equivalent to abi.encodeWithSelector(fnSelector, msg.sender, param1, param2, ...)
88-
_fnSelector,
89-
bytes32(uint256(uint160(msg.sender))), // big endian padded encoding of msg.sender, simulating abi.encodeWithSelector
90-
_data
91-
)
92-
);
82+
bytes memory nodeData = abi.encodePacked(oldCount, _to, msg.sender, _data);
9383

9484
// single hashed leaf
9585
bytes32 newInboxNode = keccak256(nodeData);

contracts/src/arbitrumToEth/VeaOutboxArbToEth.sol

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

3-
/// @custom:authors: [@jaybuidl, @shotaronowhere]
3+
/// @custom:authors: [@jaybuidl, @mani99brar, @shotaronowhere]
44
/// @custom:reviewers: []
55
/// @custom:auditors: []
66
/// @custom:bounties: []
@@ -12,6 +12,7 @@ import "../canonical/arbitrum/ISequencerInbox.sol";
1212
import "../canonical/arbitrum/IBridge.sol";
1313
import "../canonical/arbitrum/IOutbox.sol";
1414
import "../interfaces/outboxes/IVeaOutboxOnL1.sol";
15+
import "../interfaces/gateways/IReceiverGateway.sol";
1516

1617
/// @dev Vea Outbox From Arbitrum to Ethereum.
1718
/// Note: This contract is deployed on Ethereum.
@@ -355,11 +356,18 @@ contract VeaOutboxArbToEth is IVeaOutboxOnL1 {
355356
/// @param _proof The merkle proof to prove the message inclusion in the inbox state root.
356357
/// @param _msgId The zero based index of the message in the inbox.
357358
/// @param _to The address of the contract on Ethereum to call.
358-
/// @param _message The message encoded in the vea inbox as abi.encodeWithSelector(fnSelector, msg.sender, param1, param2, ...)
359-
function sendMessage(bytes32[] calldata _proof, uint64 _msgId, address _to, bytes calldata _message) external {
359+
/// @param _from The address of the contract on Arbitrum that sent the message.
360+
/// @param _message The message in the vea inbox.
361+
function sendMessage(
362+
bytes32[] calldata _proof,
363+
uint64 _msgId,
364+
address _to,
365+
address _from,
366+
bytes calldata _message
367+
) external {
360368
require(_proof.length < 64, "Proof too long.");
361369

362-
bytes32 nodeHash = keccak256(abi.encodePacked(_msgId, _to, _message));
370+
bytes32 nodeHash = keccak256(abi.encodePacked(_msgId, _to, _from, _message));
363371

364372
// double hashed leaf
365373
// avoids second order preimage attacks
@@ -407,8 +415,7 @@ contract VeaOutboxArbToEth is IVeaOutboxOnL1 {
407415
relayed[relayIndex] = replay | bytes32(1 << offset);
408416

409417
// UNTRUSTED.
410-
(bool success, ) = _to.call(_message);
411-
require(success, "Failed to call contract");
418+
IReceiverGateway(_to).receiveMessage(_from, _message);
412419

413420
emit MessageRelayed(_msgId);
414421
}
@@ -477,7 +484,7 @@ contract VeaOutboxArbToEth is IVeaOutboxOnL1 {
477484
} else {
478485
address challenger = _claim.challenger;
479486
_claim.challenger = address(0);
480-
claimHashes[_epoch] == hashClaim(_claim);
487+
claimHashes[_epoch] = hashClaim(_claim);
481488
payable(challenger).send(deposit); // User is responsible for accepting ETH.
482489
}
483490
}

contracts/src/arbitrumToGnosis/VeaInboxArbToGnosis.sol

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: MIT
22

3-
/// @custom:authors: [@jaybuidl, @shotaronowhere]
3+
/// @custom:authors: [@jaybuidl, @mani99brar, @shotaronowhere]
44
/// @custom:reviewers: []
55
/// @custom:auditors: []
66
/// @custom:bounties: []
@@ -69,27 +69,17 @@ contract VeaInboxArbToGnosis is IVeaInbox {
6969
/// `O(log(count))` where count is the number of messages already sent.
7070
/// Amortized cost is constant.
7171
/// Note: See docs for details how inbox manages merkle tree state.
72-
/// @param _to The address of the contract on the receiving chain which receives the calldata.
73-
/// @param _fnSelector The function selector of the receiving contract.
74-
/// @param _data The message calldata, abi.encode(param1, param2, ...)
72+
/// @param _to The address of the contract on the receiving chain which receives the message.
73+
/// @param _data The message data for the receiving gateway.
7574
/// @return msgId The zero based index of the message in the inbox.
76-
function sendMessage(address _to, bytes4 _fnSelector, bytes memory _data) external override returns (uint64) {
75+
function sendMessage(address _to, bytes calldata _data) external override returns (uint64) {
7776
uint64 oldCount = count;
7877

7978
// Given arbitrum's speed limit of 7 million gas / second, it would take atleast 8 million years of full blocks to overflow.
8079
// It *should* be impossible to overflow, but we check to be safe when appending to the tree.
8180
require(oldCount < type(uint64).max, "Inbox is full.");
8281

83-
bytes memory nodeData = abi.encodePacked(
84-
oldCount,
85-
_to,
86-
// _data is abi.encode(param1, param2, ...), we need to encode it again to get the correct leaf data
87-
abi.encodePacked( // equivalent to abi.encodeWithSelector(fnSelector, msg.sender, param1, param2, ...)
88-
_fnSelector,
89-
bytes32(uint256(uint160(msg.sender))), // big endian padded encoding of msg.sender, simulating abi.encodeWithSelector
90-
_data
91-
)
92-
);
82+
bytes memory nodeData = abi.encodePacked(oldCount, _to, msg.sender, _data);
9383

9484
// single hashed leaf
9585
bytes32 newInboxNode = keccak256(nodeData);

contracts/src/arbitrumToGnosis/VeaOutboxArbToGnosis.sol

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

3-
/// @custom:authors: [@jaybuidl, @shotaronowhere]
3+
/// @custom:authors: [@jaybuidl, @mani99brar, @shotaronowhere]
44
/// @custom:reviewers: []
55
/// @custom:auditors: []
66
/// @custom:bounties: []
@@ -12,6 +12,7 @@ import "../canonical/gnosis-chain/IAMB.sol";
1212
import "../interfaces/outboxes/IVeaOutboxOnL1.sol";
1313
import "../interfaces/updaters/ISequencerDelayUpdatable.sol";
1414
import "../interfaces/tokens/gnosis/IWETH.sol";
15+
import "../interfaces/gateways/IReceiverGateway.sol";
1516

1617
/// @dev Vea Outbox From Arbitrum to Gnosis.
1718
/// Note: This contract is deployed on Gnosis.
@@ -299,11 +300,18 @@ contract VeaOutboxArbToGnosis is IVeaOutboxOnL1, ISequencerDelayUpdatable {
299300
/// @param _proof The merkle proof to prove the message inclusion in the inbox state root.
300301
/// @param _msgId The zero based index of the message in the inbox.
301302
/// @param _to The address of the contract on Gnosis to call.
302-
/// @param _message The message encoded in the vea inbox as abi.encodeWithSelector(fnSelector, msg.sender, param1, param2, ...)
303-
function sendMessage(bytes32[] calldata _proof, uint64 _msgId, address _to, bytes calldata _message) external {
303+
/// @param _from The address of the contract on Arbitrum that sent the message.
304+
/// @param _message The message in the vea inbox
305+
function sendMessage(
306+
bytes32[] calldata _proof,
307+
uint64 _msgId,
308+
address _to,
309+
address _from,
310+
bytes calldata _message
311+
) external {
304312
require(_proof.length < 64, "Proof too long.");
305313

306-
bytes32 nodeHash = keccak256(abi.encodePacked(_msgId, _to, _message));
314+
bytes32 nodeHash = keccak256(abi.encodePacked(_msgId, _to, _from, _message));
307315

308316
// double hashed leaf
309317
// avoids second order preimage attacks
@@ -351,8 +359,7 @@ contract VeaOutboxArbToGnosis is IVeaOutboxOnL1, ISequencerDelayUpdatable {
351359
relayed[relayIndex] = replay | bytes32(1 << offset);
352360

353361
// UNTRUSTED.
354-
(bool success, ) = _to.call(_message);
355-
require(success, "Failed to call contract");
362+
IReceiverGateway(_to).receiveMessage(_from, _message);
356363

357364
emit MessageRelayed(_msgId);
358365
}
@@ -421,7 +428,7 @@ contract VeaOutboxArbToGnosis is IVeaOutboxOnL1, ISequencerDelayUpdatable {
421428
} else {
422429
address challenger = _claim.challenger;
423430
_claim.challenger = address(0);
424-
claimHashes[_epoch] == hashClaim(_claim);
431+
claimHashes[_epoch] = hashClaim(_claim);
425432
require(weth.transfer(challenger, deposit), "Failed WETH transfer."); // should revert on errors, but we check return value anyways
426433
}
427434
}

contracts/src/gnosisToArbitrum/VeaInboxGnosisToArb.sol

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,27 +70,17 @@ contract VeaInboxGnosisToArb is IVeaInbox {
7070
/// `O(log(count))` where count is the number of messages already sent.
7171
/// Amortized cost is constant.
7272
/// Note: See docs for details how inbox manages merkle tree state.
73-
/// @param _to The address of the contract on the receiving chain which receives the calldata.
74-
/// @param _fnSelector The function selector of the receiving contract.
75-
/// @param _data The message calldata, abi.encode(param1, param2, ...)
73+
/// @param _to The address of the contract on the receiving chain which receives the message.
74+
/// @param _data The message data for the receiving gateway.
7675
/// @return msgId The zero based index of the message in the inbox.
77-
function sendMessage(address _to, bytes4 _fnSelector, bytes memory _data) external override returns (uint64) {
76+
function sendMessage(address _to, bytes calldata _data) external override returns (uint64) {
7877
uint64 oldCount = count;
7978

8079
// Given arbitrum's speed limit of 7 million gas / second, it would take atleast 8 million years of full blocks to overflow.
8180
// It *should* be impossible to overflow, but we check to be safe when appending to the tree.
8281
require(oldCount < type(uint64).max, "Inbox is full.");
8382

84-
bytes memory nodeData = abi.encodePacked(
85-
oldCount,
86-
_to,
87-
// _data is abi.encode(param1, param2, ...), we need to encode it again to get the correct leaf data
88-
abi.encodePacked( // equivalent to abi.encodeWithSelector(fnSelector, msg.sender, param1, param2, ...)
89-
_fnSelector,
90-
bytes32(uint256(uint160(msg.sender))), // big endian padded encoding of msg.sender, simulating abi.encodeWithSelector
91-
_data
92-
)
93-
);
83+
bytes memory nodeData = abi.encodePacked(oldCount, _to, msg.sender, _data);
9484

9585
// single hashed leaf
9686
bytes32 newInboxNode = keccak256(nodeData);

contracts/src/gnosisToArbitrum/VeaOutboxGnosisToArb.sol

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pragma solidity ^0.8.24;
1010

1111
import "../interfaces/outboxes/IVeaOutboxOnL2.sol";
1212
import "../canonical/arbitrum/AddressAliasHelper.sol";
13+
import "../interfaces/gateways/IReceiverGateway.sol";
1314

1415
/// @dev Vea Outbox From Gnosis to Arbitrum.
1516
/// Note: This contract is deployed on Arbitrum.
@@ -285,11 +286,18 @@ contract VeaOutboxGnosisToArb is IVeaOutboxOnL2 {
285286
/// @param _proof The merkle proof to prove the message inclusion in the inbox state root.
286287
/// @param _msgId The zero based index of the message in the inbox.
287288
/// @param _to The address of the contract on Arbitrum to call.
288-
/// @param _message The message encoded in the vea inbox as abi.encodeWithSelector(fnSelector, msg.sender, param1, param2, ...)
289-
function sendMessage(bytes32[] memory _proof, uint64 _msgId, address _to, bytes memory _message) external {
289+
/// @param _from The address of the message sender
290+
/// @param _message The message in the vea inbox
291+
function sendMessage(
292+
bytes32[] memory _proof,
293+
uint64 _msgId,
294+
address _to,
295+
address _from,
296+
bytes memory _message
297+
) external {
290298
require(_proof.length < 64, "Proof too long.");
291299

292-
bytes32 nodeHash = keccak256(abi.encodePacked(_msgId, _to, _message));
300+
bytes32 nodeHash = keccak256(abi.encodePacked(_msgId, _to, _from, _message));
293301

294302
// double hashed leaf
295303
// avoids second order preimage attacks
@@ -337,8 +345,7 @@ contract VeaOutboxGnosisToArb is IVeaOutboxOnL2 {
337345
relayed[relayIndex] = replay | bytes32(1 << offset);
338346

339347
// UNTRUSTED.
340-
(bool success, ) = _to.call(_message);
341-
require(success, "Failed to call contract");
348+
IReceiverGateway(_to).receiveMessage(_from, _message);
342349

343350
emit MessageRelayed(_msgId);
344351
}

contracts/src/interfaces/gateways/IReceiverGateway.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ interface IReceiverGateway {
1212
function veaOutbox() external view returns (address);
1313

1414
function senderGateway() external view returns (address);
15+
16+
function receiveMessage(address msgSender, bytes calldata msgData) external;
1517
}

contracts/src/interfaces/inboxes/IVeaInbox.sol

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ pragma solidity ^0.8.24;
1111
interface IVeaInbox {
1212
/// @dev Sends an arbitrary message to receiving chain.
1313
/// Note: Calls authenticated by receiving gateway checking the sender argument.
14-
/// @param _to The cross-domain contract address which receives the calldata.
15-
/// @param _fnSelection The function selector of the receiving contract.
16-
/// @param _data The message calldata, abi.encode(...)
14+
/// @param _to The cross-domain contract address which receives the message.
15+
/// @param _data The message data.
1716
/// @return msgId The index of the message in the inbox, as a message Id, needed to relay the message.
18-
function sendMessage(address _to, bytes4 _fnSelection, bytes memory _data) external returns (uint64 msgId);
17+
function sendMessage(address _to, bytes calldata _data) external returns (uint64 msgId);
1918

2019
/// @dev Snapshots can be saved a maximum of once per epoch.
2120
/// Saves snapshot of state root.

contracts/src/interfaces/outboxes/IVeaOutboxOnL1.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,21 @@ pragma solidity ^0.8.24;
1111
import "../types/VeaClaim.sol";
1212

1313
/// @dev Interface of the Vea Outbox on L1 chains like Ethereum, Gnosis, Polygon POS where storage is expensive.
14+
1415
interface IVeaOutboxOnL1 {
1516
/// @dev Verifies and relays the message.
16-
/// Note: Gateways expect first argument of message call to be the arbitrum message sender, used for authentication.
1717
/// @param _proof The merkle proof to prove the message.
1818
/// @param _msgId The zero based index of the message in the inbox.
1919
/// @param _to The address to send the message to.
20+
/// @param _from The address of the contract on L2 that sent the message, used for authentication.
2021
/// @param _message The message to relay.
21-
function sendMessage(bytes32[] calldata _proof, uint64 _msgId, address _to, bytes calldata _message) external;
22+
function sendMessage(
23+
bytes32[] calldata _proof,
24+
uint64 _msgId,
25+
address _to,
26+
address _from,
27+
bytes calldata _message
28+
) external;
2229

2330
/// @dev Resolves any challenge of the optimistic claim for 'epoch' using the canonical bridge.
2431
/// Note: Access restricted to canonical bridge.

contracts/src/interfaces/outboxes/IVeaOutboxOnL2.sol

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@ interface IVeaOutboxOnL2 {
1515
/// @param _proof The merkle proof to prove the message.
1616
/// @param _msgId The zero based index of the message in the inbox.
1717
/// @param _to The address to send the message to.
18+
/// @param _from The address of the message sender.
1819
/// @param _message The message to relay.
19-
function sendMessage(bytes32[] calldata _proof, uint64 _msgId, address _to, bytes calldata _message) external;
20+
function sendMessage(
21+
bytes32[] calldata _proof,
22+
uint64 _msgId,
23+
address _to,
24+
address _from,
25+
bytes calldata _message
26+
) external;
2027

2128
/// @dev Resolves any challenge of the optimistic claim for 'epoch' using the canonical bridge.
2229
/// Note: Access restricted to canonical bridge.

0 commit comments

Comments
 (0)