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

131 lines
5.5 KiB
Solidity

pragma solidity 0.4.24;
import "../../interfaces/IAMBInformationReceiver.sol";
import "./BasicHomeAMB.sol";
/**
* @title AsyncInformationProcessor
* @dev Functionality for making and processing async calls on Home side of the AMB.
*/
contract AsyncInformationProcessor is BasicHomeAMB {
event UserRequestForInformation(
bytes32 indexed messageId,
bytes32 indexed requestSelector,
address indexed sender,
bytes data
);
event SignedForInformation(address indexed signer, bytes32 indexed messageId);
event InformationRetrieved(bytes32 indexed messageId, bool status, bool callbackStatus);
event EnabledAsyncRequestSelector(bytes32 indexed requestSelector, bool enable);
/**
* @dev Makes an asynchronous request to get information from the opposite network.
* Call result will be returned later to the callee, by using the onInformationReceived(bytes) callback function.
* @param _requestSelector selector for the async request.
* @param _data payload for the given selector
*/
function requireToGetInformation(bytes32 _requestSelector, bytes _data) external returns (bytes32) {
// it is not allowed to pass messages while other messages are processed
// if other is not explicitly configured
require(messageId() == bytes32(0) || allowReentrantRequests());
// only contracts are allowed to call this method, since EOA won't be able to receive a callback.
require(AddressUtils.isContract(msg.sender));
require(isAsyncRequestSelectorEnabled(_requestSelector));
bytes32 _messageId = _getNewMessageId(sourceChainId());
_setAsyncRequestSender(_messageId, msg.sender);
emit UserRequestForInformation(_messageId, _requestSelector, msg.sender, _data);
return _messageId;
}
/**
* Tells if the specific async request selector is allowed to be used and supported by the bridge oracles.
* @param _requestSelector selector for the async request.
* @return true, if selector is allowed to be used.
*/
function isAsyncRequestSelectorEnabled(bytes32 _requestSelector) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("enableRequestSelector", _requestSelector))];
}
/**
* Enables or disables the specific async request selector.
* Only owner can call this method.
* @param _requestSelector selector for the async request.
* @param _enable true, if the selector should be allowed.
*/
function enableAsyncRequestSelector(bytes32 _requestSelector, bool _enable) external onlyOwner {
boolStorage[keccak256(abi.encodePacked("enableRequestSelector", _requestSelector))] = _enable;
emit EnabledAsyncRequestSelector(_requestSelector, _enable);
}
/**
* @dev Submits result of the async call.
* Only validators are allowed to call this method.
* Once enough confirmations are collected, callback function is called.
* @param _messageId unique id of the request that was previously made.
* @param _status true, if JSON-RPC request succeeded, false otherwise.
* @param _result call result returned by the other side of the bridge.
*/
function confirmInformation(bytes32 _messageId, bool _status, bytes _result) external onlyValidator {
bytes32 hashMsg = keccak256(abi.encodePacked(_messageId, _status, _result));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
// Duplicated confirmations
require(!affirmationsSigned(hashSender));
setAffirmationsSigned(hashSender, true);
uint256 signed = numAffirmationsSigned(hashMsg);
require(!isAlreadyProcessed(signed));
// the check above assumes that the case when the value could be overflew will not happen in the addition operation below
signed = signed + 1;
setNumAffirmationsSigned(hashMsg, signed);
emit SignedForInformation(msg.sender, _messageId);
if (signed >= requiredSignatures()) {
setNumAffirmationsSigned(hashMsg, markAsProcessed(signed));
address sender = _restoreAsyncRequestSender(_messageId);
bytes memory data = abi.encodeWithSelector(
IAMBInformationReceiver(address(0)).onInformationReceived.selector,
_messageId,
_status,
_result
);
uint256 gas = maxGasPerTx();
require((gasleft() * 63) / 64 > gas);
bool callbackStatus = sender.call.gas(gas)(data);
emit InformationRetrieved(_messageId, _status, callbackStatus);
}
}
/**
* Internal function for saving async request sender for future use.
* @param _messageId id of the sent async request.
* @param _sender address of the request sender, receiver of the callback.
*/
function _setAsyncRequestSender(bytes32 _messageId, address _sender) internal {
addressStorage[keccak256(abi.encodePacked("asyncSender", _messageId))] = _sender;
}
/**
* Internal function for restoring async request sender information.
* @param _messageId id of the sent async request.
* @return address of async request sender and callback receiver.
*/
function _restoreAsyncRequestSender(bytes32 _messageId) internal returns (address) {
bytes32 hash = keccak256(abi.encodePacked("asyncSender", _messageId));
address sender = addressStorage[hash];
require(sender != address(0));
delete addressStorage[hash];
return sender;
}
}