tokenbridge-contracts/contracts/upgradeable_contracts/arbitrary_message/MessageProcessor.sol

254 lines
12 KiB
Solidity

pragma solidity 0.4.24;
import "../../upgradeability/EternalStorage.sol";
import "../../libraries/Bytes.sol";
contract MessageProcessor is EternalStorage {
/**
* @dev Returns a status of the message that came from the other side.
* @param _messageId id of the message from the other side that triggered a call.
* @return true if call executed successfully.
*/
function messageCallStatus(bytes32 _messageId) external view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("messageCallStatus", _messageId))];
}
/**
* @dev Sets a status of the message that came from the other side.
* @param _messageId id of the message from the other side that triggered a call.
* @param _status execution status, true if executed successfully.
*/
function setMessageCallStatus(bytes32 _messageId, bool _status) internal {
boolStorage[keccak256(abi.encodePacked("messageCallStatus", _messageId))] = _status;
}
/**
* @dev Returns a data hash of the failed message that came from the other side.
* NOTE: dataHash was used previously to identify outgoing message before AMB message id was introduced.
* It is kept for backwards compatibility with old mediators contracts.
* @param _messageId id of the message from the other side that triggered a call.
* @return keccak256 hash of message data.
*/
function failedMessageDataHash(bytes32 _messageId) external view returns (bytes32) {
return bytes32(uintStorage[keccak256(abi.encodePacked("failedMessageDataHash", _messageId))]);
}
/**
* @dev Sets a data hash of the failed message that came from the other side.
* NOTE: dataHash was used previously to identify outgoing message before AMB message id was introduced.
* It is kept for backwards compatibility with old mediators contracts.
* @param _messageId id of the message from the other side that triggered a call.
* @param data of the processed message.
*/
function setFailedMessageDataHash(bytes32 _messageId, bytes data) internal {
uintStorage[keccak256(abi.encodePacked("failedMessageDataHash", _messageId))] = uint256(keccak256(data));
}
/**
* @dev Returns a receiver address of the failed message that came from the other side.
* @param _messageId id of the message from the other side that triggered a call.
* @return receiver address.
*/
function failedMessageReceiver(bytes32 _messageId) external view returns (address) {
return addressStorage[keccak256(abi.encodePacked("failedMessageReceiver", _messageId))];
}
/**
* @dev Sets a sender address of the failed message that came from the other side.
* @param _messageId id of the message from the other side that triggered a call.
* @param _receiver address of the receiver.
*/
function setFailedMessageReceiver(bytes32 _messageId, address _receiver) internal {
addressStorage[keccak256(abi.encodePacked("failedMessageReceiver", _messageId))] = _receiver;
}
/**
* @dev Returns a sender address of the failed message that came from the other side.
* @param _messageId id of the message from the other side that triggered a call.
* @return sender address on the other side.
*/
function failedMessageSender(bytes32 _messageId) external view returns (address) {
return addressStorage[keccak256(abi.encodePacked("failedMessageSender", _messageId))];
}
/**
* @dev Sets a sender address of the failed message that came from the other side.
* @param _messageId id of the message from the other side that triggered a call.
* @param _sender address of the sender on the other side.
*/
function setFailedMessageSender(bytes32 _messageId, address _sender) internal {
addressStorage[keccak256(abi.encodePacked("failedMessageSender", _messageId))] = _sender;
}
/**
* @dev Returns an address of the sender on the other side for the currently processed message.
* Can be used by executors for getting other side caller address.
* @return address of the sender on the other side.
*/
function messageSender() external view returns (address sender) {
assembly {
// Even though this is not the same as addressStorage[keccak256(abi.encodePacked("messageSender"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
sender := sload(0x7b58b2a669d8e0992eae9eaef641092c0f686fd31070e7236865557fa1571b5b) // keccak256(abi.encodePacked("messageSender"))
}
}
/**
* @dev Sets an address of the sender on the other side for the currently processed message.
* @param _sender address of the sender on the other side.
*/
function setMessageSender(address _sender) internal {
assembly {
// Even though this is not the same as addressStorage[keccak256(abi.encodePacked("messageSender"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
sstore(0x7b58b2a669d8e0992eae9eaef641092c0f686fd31070e7236865557fa1571b5b, _sender) // keccak256(abi.encodePacked("messageSender"))
}
}
/**
* @dev Returns an id of the currently processed message.
* @return id of the message that originated on the other side.
*/
function messageId() public view returns (bytes32 id) {
assembly {
// Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageId"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
id := sload(0xe34bb2103dc34f2c144cc216c132d6ffb55dac57575c22e089161bbe65083304) // keccak256(abi.encodePacked("messageId"))
}
}
/**
* @dev Returns an id of the currently processed message.
* NOTE: transactionHash was used previously to identify incoming message before AMB message id was introduced.
* It is kept for backwards compatibility with old mediators contracts, although it doesn't return txHash anymore.
* @return id of the message that originated on the other side.
*/
function transactionHash() external view returns (bytes32) {
return messageId();
}
/**
* @dev Sets a message id of the currently processed message.
* @param _messageId id of the message that originated on the other side.
*/
function setMessageId(bytes32 _messageId) internal {
assembly {
// Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageId"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
sstore(0xe34bb2103dc34f2c144cc216c132d6ffb55dac57575c22e089161bbe65083304, _messageId) // keccak256(abi.encodePacked("messageId"))
}
}
/**
* @dev Returns an originating chain id of the currently processed message.
* @return source chain id of the message that originated on the other side.
*/
function messageSourceChainId() external view returns (uint256 id) {
assembly {
// Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageSourceChainId"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
id := sload(0x7f0fcd9e49860f055dd0c1682d635d309ecb5e3011654c716d9eb59a7ddec7d2) // keccak256(abi.encodePacked("messageSourceChainId"))
}
}
/**
* @dev Sets an originating chain id of the currently processed message.
* @param _sourceChainId source chain id of the message that originated on the other side.
*/
function setMessageSourceChainId(uint256 _sourceChainId) internal {
assembly {
// Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageSourceChainId"))],
// since solidity mapping introduces another level of addressing, such slot change is safe
// for temporary variables which are cleared at the end of the call execution.
sstore(0x7f0fcd9e49860f055dd0c1682d635d309ecb5e3011654c716d9eb59a7ddec7d2, _sourceChainId) // keccak256(abi.encodePacked("messageSourceChainId"))
}
}
/**
* @dev Processes received message. Makes a call to the message executor,
* sets dataHash, receive, sender variables for failed messages.
* @param _sender sender address on the other side.
* @param _executor address of an executor.
* @param _messageId id of the processed message.
* @param _gasLimit gas limit for a call to executor.
* @param _sourceChainId source chain id is of the received message.
* @param _data calldata for a call to executor.
*/
function processMessage(
address _sender,
address _executor,
bytes32 _messageId,
uint256 _gasLimit,
uint8, /* dataType */
uint256 _sourceChainId,
bytes memory _data
) internal {
bool status = _passMessage(_sender, _executor, _data, _gasLimit, _messageId, _sourceChainId);
setMessageCallStatus(_messageId, status);
if (!status) {
setFailedMessageDataHash(_messageId, _data);
setFailedMessageReceiver(_messageId, _executor);
setFailedMessageSender(_messageId, _sender);
}
emitEventOnMessageProcessed(_sender, _executor, _messageId, status);
}
/**
* @dev Makes a call to the message executor.
* @param _sender sender address on the other side.
* @param _contract address of an executor contract.
* @param _data calldata for a call to executor.
* @param _gas gas limit for a call to executor. 2^32 - 1, if caller will pass all available gas for the execution.
* @param _messageId id of the processed message.
* @param _sourceChainId source chain id is of the received message.
*/
function _passMessage(
address _sender,
address _contract,
bytes _data,
uint256 _gas,
bytes32 _messageId,
uint256 _sourceChainId
) internal returns (bool) {
setMessageSender(_sender);
setMessageId(_messageId);
setMessageSourceChainId(_sourceChainId);
// After EIP-150, max gas cost allowed to be passed to the internal call is equal to the 63/64 of total gas left.
// In reality, min(gasLimit, 63/64 * gasleft()) will be used as the call gas limit.
// Imagine a situation, when message requires 10000000 gas to be executed successfully.
// Also suppose, that at this point, gasleft() is equal to 10158000, so the callee will receive ~ 10158000 * 63 / 64 = 9999300 gas.
// That amount of gas is not enough, so the call will fail. At the same time,
// even if the callee failed the bridge contract still has ~ 158000 gas to
// finish its execution and it will be enough. The internal call fails but
// only because the oracle provides incorrect gas limit for the transaction
// This check is needed here in order to force contract to pass exactly the requested amount of gas.
// Avoiding it may lead to the unwanted message failure in some extreme cases.
require(_gas == 0xffffffff || (gasleft() * 63) / 64 > _gas);
bool status = _contract.call.gas(_gas)(_data);
_validateExecutionStatus(status);
setMessageSender(address(0));
setMessageId(bytes32(0));
setMessageSourceChainId(0);
return status;
}
/**
* @dev Validates message execution status. In simplest case, does nothing.
* @param _status message execution status.
*/
function _validateExecutionStatus(bool _status) internal {
(_status);
}
/* solcov ignore next */
function emitEventOnMessageProcessed(address sender, address executor, bytes32 messageId, bool status) internal;
}