131 lines
5.5 KiB
Solidity
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;
|
|
}
|
|
}
|