pragma solidity ^0.5.0; import './openzeppelin-solidity/contracts/token/ERC20/IERC20.sol'; contract Bridge { event NewEpoch(uint indexed epoch); event KeygenCompleted(uint indexed epoch, uint x, uint y); event ReceivedTokens(address from, string recipient, uint value); // pass epoch and params in this event address[] public validators; address[] public nextValidators; address[] public savedNextValidators; mapping(bytes32 => uint) public confirmationsCount; mapping(bytes32 => bool) public confirmations; mapping(bytes32 => uint) public dbTransferCount; mapping(bytes32 => bool) public dbTransfer; mapping(bytes32 => uint) public votesCount; mapping(bytes32 => bool) public votes; uint public x; uint public y; bool public ready; uint public threshold; uint public nextThreshold; uint public epoch; constructor(uint _threshold, uint _parties, address[] memory _validators, address _tokenContract) public { require(_parties > 0); require(_threshold < _parties); require(_validators.length == _parties); tokenContract = IERC20(_tokenContract); epoch = 1; ready = false; nextThreshold = _threshold; savedNextValidators = _validators; emit NewEpoch(epoch); } IERC20 public tokenContract; function requestAffirmation(uint value, string memory recipient) public { require(ready, "Current epoch is not ready"); tokenContract.transferFrom(msg.sender, address(this), value); emit ReceivedTokens(msg.sender, recipient, value); } function transfer(bytes32 hash, address to, uint value) public { uint partyId = getPartyId(); require(partyId != 0, "Not a validator"); require(!dbTransfer[keccak256(abi.encodePacked(hash, msg.sender, to, value))], "Already voted"); dbTransfer[keccak256(abi.encodePacked(hash, msg.sender, to, value))] = true; if (++dbTransferCount[keccak256(abi.encodePacked(hash, to, value))] == threshold + 1) tokenContract.transfer(to, value); } function confirm(uint _x, uint _y) public { uint partyId = getNextPartyId(msg.sender); require(partyId != 0, "Not a next validator"); require(!confirmations[keccak256(abi.encodePacked(epoch, partyId, _x, _y))], "Already confirmed"); confirmations[keccak256(abi.encodePacked(epoch, partyId, _x, _y))] = true; if (++confirmationsCount[keccak256(abi.encodePacked(epoch, _x, _y))] == nextParties()) { x = _x; y = _y; validators = savedNextValidators; nextValidators = savedNextValidators; threshold = nextThreshold; ready = true; emit KeygenCompleted(epoch, x, y); } } function parties() view public returns (uint) { return validators.length; } function nextParties() view public returns (uint) { return savedNextValidators.length; } function getPartyId() view public returns (uint) { return getPartyId(msg.sender); } function getPartyId(address a) view public returns (uint) { for (uint i = 0; i < parties(); i++) { if (validators[i] == a) return i + 1; } return 0; } function getNextPartyId(address a) view public returns (uint) { for (uint i = 0; i < nextParties(); i++) { if (savedNextValidators[i] == a) return i + 1; } return 0; } function getValidatorsArray() view public returns (address[] memory) { return validators; } function getNextValidatorsArray() view public returns (address[] memory) { return savedNextValidators; } // Send current epoch in votes? function voteAddValidator(address validator) public { require(getPartyId() != 0, "Not a current validator"); require(getNextPartyId(validator) == 0, "Already a validator"); require(!votes[keccak256(abi.encodePacked(uint(1), epoch, msg.sender, validator))], "Already voted"); votes[keccak256(abi.encodePacked(uint(1), epoch, msg.sender, validator))] = true; if (++votesCount[keccak256(abi.encodePacked(uint(1), epoch, validator))] == threshold + 1) { nextValidators.push(validator); } } function voteRemoveValidator(address validator) public { require(getPartyId() != 0, "Not a current validator"); require(getNextPartyId(validator) != 0, "Already not a validator"); require(!votes[keccak256(abi.encodePacked(uint(2), epoch, msg.sender, validator))], "Already voted"); votes[keccak256(abi.encodePacked(uint(2), epoch, msg.sender, validator))] = true; if (++votesCount[keccak256(abi.encodePacked(uint(2), epoch, validator))] == threshold + 1) { _removeValidator(validator); } } function _removeValidator(address validator) private { for (uint i = 0; i < nextValidators.length - 1; i++) { if (nextValidators[i] == validator) { nextValidators[i] = nextValidators[nextValidators.length - 1]; } } delete nextValidators[nextValidators.length - 1]; nextValidators.length--; } function voteChangeThreshold(uint _threshold) public { require(getPartyId() != 0, "Not a current validator"); require(!votes[keccak256(abi.encodePacked(uint(3), epoch, msg.sender, threshold))], "Already voted"); votes[keccak256(abi.encodePacked(uint(3), epoch, msg.sender, _threshold))] = true; if (++votesCount[keccak256(abi.encodePacked(uint(3), epoch, _threshold))] == threshold + 1) { nextThreshold = _threshold; } } function voteStartEpoch(uint newEpoch) public { require(newEpoch == epoch + 1, "Wrong epoch number"); require(getPartyId() != 0, "Not a current validator"); require(!votes[keccak256(abi.encodePacked(uint(4), epoch, msg.sender))], "Voted already"); votes[keccak256(abi.encodePacked(uint(4), epoch, msg.sender))] = true; if (++votesCount[keccak256(abi.encodePacked(uint(4), epoch))] == threshold + 1) { ready = false; epoch++; savedNextValidators = nextValidators; emit NewEpoch(epoch); } } }