eth-to-bnc-bridge/src/deploy/deploy-home/contracts/Bridge.sol

178 lines
6.3 KiB
Solidity

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);
}
}
}