|
|
|
@ -13,8 +13,8 @@ import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol"; |
|
|
|
|
/** |
|
|
|
|
* @title Inbox |
|
|
|
|
* @author Celo Labs Inc. |
|
|
|
|
* @notice Track root updates on Outbox, |
|
|
|
|
* prove and dispatch messages to end recipients. |
|
|
|
|
* @notice Track root updates on Outbox, prove and dispatch messages to end |
|
|
|
|
* recipients. |
|
|
|
|
*/ |
|
|
|
|
contract Inbox is Version0, Common { |
|
|
|
|
// ============ Libraries ============ |
|
|
|
@ -36,13 +36,6 @@ contract Inbox is Version0, Common { |
|
|
|
|
Processed |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============ Immutables ============ |
|
|
|
|
|
|
|
|
|
// Minimum gas for message processing |
|
|
|
|
uint256 public immutable PROCESS_GAS; |
|
|
|
|
// Reserved gas (to ensure tx completes in case message processing runs out) |
|
|
|
|
uint256 public immutable RESERVE_GAS; |
|
|
|
|
|
|
|
|
|
// ============ Public Storage ============ |
|
|
|
|
|
|
|
|
|
// Domain of outbox chain |
|
|
|
@ -62,28 +55,13 @@ contract Inbox is Version0, Common { |
|
|
|
|
/** |
|
|
|
|
* @notice Emitted when message is processed |
|
|
|
|
* @param messageHash Hash of message that failed to process |
|
|
|
|
* @param success TRUE if the call was executed successfully, FALSE if the call reverted |
|
|
|
|
* @param returnData the return data from the external call |
|
|
|
|
*/ |
|
|
|
|
event Process( |
|
|
|
|
bytes32 indexed messageHash, |
|
|
|
|
bool indexed success, |
|
|
|
|
bytes indexed returnData |
|
|
|
|
); |
|
|
|
|
event Process(bytes32 indexed messageHash); |
|
|
|
|
|
|
|
|
|
// ============ Constructor ============ |
|
|
|
|
|
|
|
|
|
// solhint-disable-next-line no-empty-blocks |
|
|
|
|
constructor( |
|
|
|
|
uint32 _localDomain, |
|
|
|
|
uint256 _processGas, |
|
|
|
|
uint256 _reserveGas |
|
|
|
|
) Common(_localDomain) { |
|
|
|
|
require(_processGas >= 850_000, "!process gas"); |
|
|
|
|
require(_reserveGas >= 15_000, "!reserve gas"); |
|
|
|
|
PROCESS_GAS = _processGas; |
|
|
|
|
RESERVE_GAS = _reserveGas; |
|
|
|
|
} |
|
|
|
|
constructor(uint32 _localDomain) Common(_localDomain) {} |
|
|
|
|
|
|
|
|
|
// ============ Initializer ============ |
|
|
|
|
|
|
|
|
@ -153,12 +131,10 @@ contract Inbox is Version0, Common { |
|
|
|
|
* message payload to end recipient. |
|
|
|
|
* @dev Recipient must implement a `handle` method (refer to IMessageRecipient.sol) |
|
|
|
|
* Reverts if formatted message's destination domain is not the Inbox's domain, |
|
|
|
|
* if message has not been proven, |
|
|
|
|
* or if not enough gas is provided for the dispatch transaction. |
|
|
|
|
* if message has not been proven, or if the dispatch transaction fails. |
|
|
|
|
* @param _message Formatted message |
|
|
|
|
* @return _success TRUE iff dispatch transaction succeeded |
|
|
|
|
*/ |
|
|
|
|
function process(bytes memory _message) public returns (bool _success) { |
|
|
|
|
function process(bytes memory _message) public { |
|
|
|
|
bytes29 _m = _message.ref(0); |
|
|
|
|
// ensure message was meant for this domain |
|
|
|
|
require(_m.destination() == localDomain, "!destination"); |
|
|
|
@ -170,54 +146,10 @@ contract Inbox is Version0, Common { |
|
|
|
|
entered = 0; |
|
|
|
|
// update message status as processed |
|
|
|
|
messages[_messageHash] = MessageStatus.Processed; |
|
|
|
|
// A call running out of gas TYPICALLY errors the whole tx. We want to |
|
|
|
|
// a) ensure the call has a sufficient amount of gas to make a |
|
|
|
|
// meaningful state change. |
|
|
|
|
// b) ensure that if the subcall runs out of gas, that the tx as a whole |
|
|
|
|
// does not revert (i.e. we still mark the message processed) |
|
|
|
|
// To do this, we require that we have enough gas to process |
|
|
|
|
// and still return. We then delegate only the minimum processing gas. |
|
|
|
|
require(gasleft() >= PROCESS_GAS + RESERVE_GAS, "!gas"); |
|
|
|
|
// get the message recipient |
|
|
|
|
address _recipient = _m.recipientAddress(); |
|
|
|
|
// set up for assembly call |
|
|
|
|
uint256 _toCopy; |
|
|
|
|
uint256 _maxCopy = 256; |
|
|
|
|
uint256 _gas = PROCESS_GAS; |
|
|
|
|
// allocate memory for returndata |
|
|
|
|
bytes memory _returnData = new bytes(_maxCopy); |
|
|
|
|
bytes memory _calldata = abi.encodeWithSignature( |
|
|
|
|
"handle(uint32,bytes32,bytes)", |
|
|
|
|
_m.origin(), |
|
|
|
|
_m.sender(), |
|
|
|
|
_m.body().clone() |
|
|
|
|
); |
|
|
|
|
// dispatch message to recipient |
|
|
|
|
// by assembly calling "handle" function |
|
|
|
|
// we call via assembly to avoid memcopying a very large returndata |
|
|
|
|
// returned by a malicious contract |
|
|
|
|
assembly { |
|
|
|
|
_success := call( |
|
|
|
|
_gas, // gas |
|
|
|
|
_recipient, // recipient |
|
|
|
|
0, // ether value |
|
|
|
|
add(_calldata, 0x20), // inloc |
|
|
|
|
mload(_calldata), // inlen |
|
|
|
|
0, // outloc |
|
|
|
|
0 // outlen |
|
|
|
|
) |
|
|
|
|
// limit our copy to 256 bytes |
|
|
|
|
_toCopy := returndatasize() |
|
|
|
|
if gt(_toCopy, _maxCopy) { |
|
|
|
|
_toCopy := _maxCopy |
|
|
|
|
} |
|
|
|
|
// Store the length of the copied bytes |
|
|
|
|
mstore(_returnData, _toCopy) |
|
|
|
|
// copy the bytes from returndata[0:_toCopy] |
|
|
|
|
returndatacopy(add(_returnData, 0x20), 0, _toCopy) |
|
|
|
|
} |
|
|
|
|
IMessageRecipient _recipient = IMessageRecipient(_m.recipientAddress()); |
|
|
|
|
_recipient.handle(_m.origin(), _m.sender(), _m.body().clone()); |
|
|
|
|
// emit process results |
|
|
|
|
emit Process(_messageHash, _success, _returnData); |
|
|
|
|
emit Process(_messageHash); |
|
|
|
|
// reset re-entrancy guard |
|
|
|
|
entered = 1; |
|
|
|
|
} |
|
|
|
|