171 lines
7.1 KiB
Solidity
171 lines
7.1 KiB
Solidity
pragma solidity 0.4.24;
|
|
|
|
import "../upgradeability/EternalStorage.sol";
|
|
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
|
|
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
|
|
import "./Validatable.sol";
|
|
import "../libraries/Message.sol";
|
|
import "./BasicBridge.sol";
|
|
import "./BasicTokenBridge.sol";
|
|
|
|
/**
|
|
* @title BasicHomeBridge
|
|
* @dev This contract implements common functionality for all vanilla bridge modes on the Home side.
|
|
*/
|
|
contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicTokenBridge {
|
|
using SafeMath for uint256;
|
|
|
|
event UserRequestForSignature(address recipient, uint256 value);
|
|
event AffirmationCompleted(address recipient, uint256 value, bytes32 transactionHash);
|
|
event SignedForUserRequest(address indexed signer, bytes32 messageHash);
|
|
event SignedForAffirmation(address indexed signer, bytes32 transactionHash);
|
|
event CollectedSignatures(
|
|
address authorityResponsibleForRelay,
|
|
bytes32 messageHash,
|
|
uint256 NumberOfCollectedSignatures
|
|
);
|
|
|
|
/**
|
|
* @dev Executes a message affirmation for some Foreign side event.
|
|
* Can be called only by a current bridge validator.
|
|
* @param recipient tokens/coins of receiver address, where the assets should be unlocked/minted.
|
|
* @param value amount of assets to unlock/mint.
|
|
* @param transactionHash reference event transaction hash on the Foreign side of the bridge.
|
|
*/
|
|
function executeAffirmation(address recipient, uint256 value, bytes32 transactionHash) external onlyValidator {
|
|
bytes32 hashMsg = keccak256(abi.encodePacked(recipient, value, transactionHash));
|
|
if (withinExecutionLimit(value)) {
|
|
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
|
|
// Duplicated affirmations
|
|
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 SignedForAffirmation(msg.sender, transactionHash);
|
|
|
|
if (signed >= requiredSignatures()) {
|
|
// If the bridge contract does not own enough tokens to transfer
|
|
// it will couse funds lock on the home side of the bridge
|
|
setNumAffirmationsSigned(hashMsg, markAsProcessed(signed));
|
|
if (value > 0) {
|
|
require(onExecuteAffirmation(recipient, value, transactionHash, hashMsg));
|
|
}
|
|
emit AffirmationCompleted(recipient, value, transactionHash);
|
|
}
|
|
} else {
|
|
onFailedAffirmation(recipient, value, transactionHash, hashMsg);
|
|
}
|
|
}
|
|
|
|
function submitSignature(bytes signature, bytes message) external onlyValidator {
|
|
// ensure that `signature` is really `message` signed by `msg.sender`
|
|
require(Message.isMessageValid(message));
|
|
require(msg.sender == Message.recoverAddressFromSignedMessage(signature, message, false));
|
|
bytes32 hashMsg = keccak256(abi.encodePacked(message));
|
|
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
|
|
|
|
uint256 signed = numMessagesSigned(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;
|
|
if (signed > 1) {
|
|
// Duplicated signatures
|
|
require(!messagesSigned(hashSender));
|
|
} else {
|
|
setMessages(hashMsg, message);
|
|
}
|
|
setMessagesSigned(hashSender, true);
|
|
|
|
bytes32 signIdx = keccak256(abi.encodePacked(hashMsg, (signed - 1)));
|
|
setSignatures(signIdx, signature);
|
|
|
|
setNumMessagesSigned(hashMsg, signed);
|
|
|
|
emit SignedForUserRequest(msg.sender, hashMsg);
|
|
|
|
uint256 reqSigs = requiredSignatures();
|
|
if (signed >= reqSigs) {
|
|
setNumMessagesSigned(hashMsg, markAsProcessed(signed));
|
|
emit CollectedSignatures(msg.sender, hashMsg, reqSigs);
|
|
|
|
onSignaturesCollected(message);
|
|
}
|
|
}
|
|
|
|
function setMessagesSigned(bytes32 _hash, bool _status) internal {
|
|
boolStorage[keccak256(abi.encodePacked("messagesSigned", _hash))] = _status;
|
|
}
|
|
|
|
/* solcov ignore next */
|
|
function onExecuteAffirmation(address, uint256, bytes32, bytes32) internal returns (bool);
|
|
|
|
/* solcov ignore next */
|
|
function onFailedAffirmation(address, uint256, bytes32, bytes32) internal;
|
|
|
|
/* solcov ignore next */
|
|
function onSignaturesCollected(bytes) internal;
|
|
|
|
function numAffirmationsSigned(bytes32 _withdrawal) public view returns (uint256) {
|
|
return uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))];
|
|
}
|
|
|
|
function setAffirmationsSigned(bytes32 _withdrawal, bool _status) internal {
|
|
boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))] = _status;
|
|
}
|
|
|
|
function setNumAffirmationsSigned(bytes32 _withdrawal, uint256 _number) internal {
|
|
uintStorage[keccak256(abi.encodePacked("numAffirmationsSigned", _withdrawal))] = _number;
|
|
}
|
|
|
|
function affirmationsSigned(bytes32 _withdrawal) public view returns (bool) {
|
|
return boolStorage[keccak256(abi.encodePacked("affirmationsSigned", _withdrawal))];
|
|
}
|
|
|
|
function signature(bytes32 _hash, uint256 _index) external view returns (bytes) {
|
|
bytes32 signIdx = keccak256(abi.encodePacked(_hash, _index));
|
|
return bytesStorage[keccak256(abi.encodePacked("signatures", signIdx))];
|
|
}
|
|
|
|
function messagesSigned(bytes32 _message) public view returns (bool) {
|
|
return boolStorage[keccak256(abi.encodePacked("messagesSigned", _message))];
|
|
}
|
|
|
|
function setSignatures(bytes32 _hash, bytes _signature) internal {
|
|
bytesStorage[keccak256(abi.encodePacked("signatures", _hash))] = _signature;
|
|
}
|
|
|
|
function setMessages(bytes32 _hash, bytes _message) internal {
|
|
bytesStorage[keccak256(abi.encodePacked("messages", _hash))] = _message;
|
|
}
|
|
|
|
function message(bytes32 _hash) external view returns (bytes) {
|
|
return bytesStorage[keccak256(abi.encodePacked("messages", _hash))];
|
|
}
|
|
|
|
function setNumMessagesSigned(bytes32 _message, uint256 _number) internal {
|
|
uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))] = _number;
|
|
}
|
|
|
|
function markAsProcessed(uint256 _v) internal pure returns (uint256) {
|
|
return _v | (2**255);
|
|
}
|
|
|
|
function isAlreadyProcessed(uint256 _number) public pure returns (bool) {
|
|
return _number & (2**255) == 2**255;
|
|
}
|
|
|
|
function numMessagesSigned(bytes32 _message) public view returns (uint256) {
|
|
return uintStorage[keccak256(abi.encodePacked("numMessagesSigned", _message))];
|
|
}
|
|
|
|
function requiredMessageLength() public pure returns (uint256) {
|
|
return Message.requiredMessageLength();
|
|
}
|
|
}
|