Refactor Bridge contract

This commit is contained in:
Kirill Fedoseev 2019-10-05 17:09:47 +03:00
parent 9f255e3ced
commit 8497af3b14
2 changed files with 104 additions and 55 deletions

View File

@ -15,20 +15,33 @@ contract Bridge {
uint y;
}
enum Status {
READY, // bridge is in ready to perform operations
VOTING, // voting for changing in next epoch, but still ready
KEYGEN, //keygen, can be cancelled
FUNDS_TRANSFER // funds transfer, cannot be cancelled
}
enum Vote {
CONFIRM_KEYGEN,
CONFIRM_FUNDS_TRANSFER,
START_VOTING,
ADD_VALIDATOR,
REMOVE_VALIDATOR,
CHANGE_THRESHOLD,
START_KEYGEN,
CANCEL_KEYGEN,
TRANSFER
}
mapping(uint => State) states;
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;
// 0 - ready
// 1 - voting for changing in next epoch, but still ready
// 2 - keygen, can be cancelled
// 3 - funds transfer, cannot be cancelled
uint public status;
Status public status;
uint public epoch;
uint public nextEpoch;
@ -40,7 +53,7 @@ contract Bridge {
tokenContract = IERC20(_tokenContract);
epoch = 0;
status = 2;
status = Status.KEYGEN;
nextEpoch = 1;
states[1] = State(validators, threshold, 0, 0);
@ -51,27 +64,27 @@ contract Bridge {
IERC20 public tokenContract;
modifier ready {
require(status == 0, "Not in ready state");
require(status == Status.READY, "Not in ready state");
_;
}
modifier readyOrVoting {
require(status < 2, "Not in ready or voting state");
require(status == Status.READY || status == Status.VOTING, "Not in ready or voting state");
_;
}
modifier voting {
require(status == 1, "Not in voting state");
require(status == Status.VOTING, "Not in voting state");
_;
}
modifier keygen {
require(status == 2, "Not in keygen state");
require(status == Status.KEYGEN, "Not in keygen state");
_;
}
modifier fundsTransfer {
require(status == 3, "Not in funds transfer state");
require(status == Status.FUNDS_TRANSFER, "Not in funds transfer state");
_;
}
@ -81,31 +94,24 @@ contract Bridge {
}
function transfer(bytes32 hash, address to, uint value) public readyOrVoting currentValidator {
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))] == getThreshold() + 1) {
dbTransferCount[keccak256(abi.encodePacked(hash, to, value))] = 2 ** 255;
if (tryVote(Vote.TRANSFER, hash, to, value)) {
tokenContract.transfer(to, value);
}
}
function confirmKeygen(uint x, uint y) public keygen {
require(getNextPartyId(msg.sender) != 0, "Not a next validator");
require(!confirmations[keccak256(abi.encodePacked(uint(1), nextEpoch, msg.sender, x, y))], "Already confirmed");
confirmations[keccak256(abi.encodePacked(uint(1), nextEpoch, msg.sender, x, y))] = true;
if (++confirmationsCount[keccak256(abi.encodePacked(uint(1), nextEpoch, x, y))] == getNextThreshold() + 1) {
confirmationsCount[keccak256(abi.encodePacked(uint(1), nextEpoch, x, y))] = 2 ** 255;
if (tryConfirm(Vote.CONFIRM_KEYGEN, x, y)) {
states[nextEpoch].x = x;
states[nextEpoch].y = y;
if (nextEpoch == 1) {
status = 0;
status = Status.READY;
epoch = nextEpoch;
emit EpochStart(epoch, x, y);
}
else {
status = 3;
status = Status.FUNDS_TRANSFER;
emit NewFundsTransfer(epoch, nextEpoch);
}
}
@ -113,12 +119,9 @@ contract Bridge {
function confirmFundsTransfer() public fundsTransfer currentValidator {
require(epoch > 0, "First epoch does not need funds transfer");
require(!confirmations[keccak256(abi.encodePacked(uint(2), nextEpoch, msg.sender))], "Already confirmed");
confirmations[keccak256(abi.encodePacked(uint(2), nextEpoch, msg.sender))] = true;
if (++confirmationsCount[keccak256(abi.encodePacked(uint(2), nextEpoch))] == getNextThreshold() + 1) {
confirmationsCount[keccak256(abi.encodePacked(uint(2), nextEpoch))] = 2 ** 255;
status = 0;
if (tryConfirm(Vote.CONFIRM_FUNDS_TRANSFER)) {
status = Status.READY;
epoch = nextEpoch;
emit EpochStart(epoch, states[epoch].x, states[epoch].y);
}
@ -183,12 +186,9 @@ contract Bridge {
}
function startVoting() public readyOrVoting currentValidator {
require(!votes[keccak256(abi.encodePacked(uint(6), nextEpoch, msg.sender))], "Already voted");
votes[keccak256(abi.encodePacked(uint(6), nextEpoch, msg.sender))] = true;
if (++votesCount[keccak256(abi.encodePacked(uint(6), nextEpoch))] == getThreshold() + 1) {
if (tryVote(Vote.START_VOTING)) {
nextEpoch++;
status = 1;
status = Status.VOTING;
states[nextEpoch].threshold = states[epoch].threshold;
states[nextEpoch].validators = states[epoch].validators;
}
@ -196,20 +196,16 @@ contract Bridge {
function voteAddValidator(address validator) public voting currentValidator {
require(getNextPartyId(validator) == 0, "Already a validator");
require(!votes[keccak256(abi.encodePacked(uint(1), nextEpoch, msg.sender, validator))], "Already voted");
votes[keccak256(abi.encodePacked(uint(1), nextEpoch, msg.sender, validator))] = true;
if (++votesCount[keccak256(abi.encodePacked(uint(1), nextEpoch, validator))] == getThreshold() + 1) {
if (tryVote(Vote.ADD_VALIDATOR, validator)) {
states[nextEpoch].validators.push(validator);
}
}
function voteRemoveValidator(address validator) public voting currentValidator {
require(getNextPartyId(validator) != 0, "Already not a validator");
require(!votes[keccak256(abi.encodePacked(uint(2), nextEpoch, msg.sender, validator))], "Already voted");
votes[keccak256(abi.encodePacked(uint(2), nextEpoch, msg.sender, validator))] = true;
if (++votesCount[keccak256(abi.encodePacked(uint(2), nextEpoch, validator))] == getThreshold() + 1) {
if (tryVote(Vote.REMOVE_VALIDATOR, validator)) {
_removeValidator(validator);
}
}
@ -226,33 +222,86 @@ contract Bridge {
}
function voteChangeThreshold(uint threshold) public voting currentValidator {
require(!votes[keccak256(abi.encodePacked(uint(3), nextEpoch, msg.sender, threshold))], "Already voted");
votes[keccak256(abi.encodePacked(uint(3), nextEpoch, msg.sender, threshold))] = true;
if (++votesCount[keccak256(abi.encodePacked(uint(3), nextEpoch, threshold))] == getThreshold() + 1) {
if (tryVote(Vote.CHANGE_THRESHOLD, threshold)) {
states[nextEpoch].threshold = threshold;
}
}
function voteStartKeygen() public voting currentValidator {
require(!votes[keccak256(abi.encodePacked(uint(4), nextEpoch + 1, msg.sender))], "Voted already");
votes[keccak256(abi.encodePacked(uint(4), nextEpoch + 1, msg.sender))] = true;
if (++votesCount[keccak256(abi.encodePacked(uint(4), nextEpoch + 1))] == getThreshold() + 1) {
status = 2;
if (tryVote(Vote.START_KEYGEN)) {
status = Status.KEYGEN;
emit NewEpoch(epoch, nextEpoch);
}
}
function voteCancelKeygen() public keygen currentValidator {
require(!votes[keccak256(abi.encodePacked(uint(5), nextEpoch, msg.sender))], "Voted already");
votes[keccak256(abi.encodePacked(uint(5), nextEpoch, msg.sender))] = true;
if (++votesCount[keccak256(abi.encodePacked(uint(5), nextEpoch))] == getThreshold() + 1) {
status = 0;
if (tryVote(Vote.CANCEL_KEYGEN)) {
status = Status.VOTING;
emit NewEpochCancelled(nextEpoch);
}
}
function tryVote(Vote voteType) private returns (bool) {
bytes32 vote = keccak256(abi.encodePacked(voteType, nextEpoch));
return putVote(vote);
}
function tryVote(Vote voteType, address addr) private returns (bool) {
bytes32 vote = keccak256(abi.encodePacked(voteType, nextEpoch, addr));
return putVote(vote);
}
function tryVote(Vote voteType, uint num) private returns (bool) {
bytes32 vote = keccak256(abi.encodePacked(voteType, nextEpoch, num));
return putVote(vote);
}
function tryVote(Vote voteType, bytes32 hash, address to, uint value) private returns (bool) {
bytes32 vote = keccak256(abi.encodePacked(voteType, hash, to, value));
return putVote(vote);
}
function tryConfirm(Vote voteType) private returns (bool) {
bytes32 vote = keccak256(abi.encodePacked(voteType, nextEpoch));
return putConfirm(vote);
}
function tryConfirm(Vote voteType, uint x, uint y) private returns (bool) {
bytes32 vote = keccak256(abi.encodePacked(voteType, nextEpoch, x, y));
return putConfirm(vote);
}
function putVote(bytes32 vote) private returns (bool) {
bytes32 personalVote = personalizeVote(vote);
require(!votes[personalVote], "Voted already");
votes[personalVote] = true;
if (votesCount[vote] == getThreshold()) {
votesCount[vote] = 2 ** 255;
return true;
} else {
votesCount[vote]++;
return false;
}
}
function putConfirm(bytes32 vote) private returns (bool) {
bytes32 personalVote = personalizeVote(vote);
require(!votes[personalVote], "Confirmed already");
votes[personalVote] = true;
if (votesCount[vote] == getNextThreshold()) {
votesCount[vote] = 2 ** 255;
return true;
} else {
votesCount[vote]++;
return false;
}
}
function personalizeVote(bytes32 vote) private view returns (bytes32) {
return keccak256(abi.encodePacked(vote, msg.sender));
}
}

View File

@ -321,7 +321,7 @@ async function voteAddValidator (req, res) {
async function voteChangeThreshold (req, res) {
console.log('Voting for changing threshold')
const query = bridge.methods.voteChangeThreshold(req.params.theshold)
const query = bridge.methods.voteChangeThreshold(req.params.threshold)
try {
await homeSendQuery(query)
} catch (e) {