-
Notifications
You must be signed in to change notification settings - Fork 0
/
SlashingVoting.sol
155 lines (114 loc) · 5.72 KB
/
SlashingVoting.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@solarity/solidity-lib/contracts-registry/AbstractDependant.sol";
import "@solarity/solidity-lib/utils/Globals.sol";
import "../interfaces/core/IContractsRegistry.sol";
import "../interfaces/system/IDKG.sol";
import "../interfaces/system/IStaking.sol";
import "../interfaces/system/ISlashingVoting.sol";
contract SlashingVoting is ISlashingVoting, Initializable, AbstractDependant {
using EnumerableSet for EnumerableSet.AddressSet;
IContractsRegistry internal _contractsRegistry;
IStaking internal _staking;
IDKG internal _dkg;
uint256 public lastProposalId;
uint256 public votingThresholdPercentage;
mapping(uint256 => SlashingProposalData) internal _proposalsData;
mapping(address => uint256) internal _pendingSlashingProposals;
modifier onlySigner() {
_onlySigner();
_;
}
modifier onlyActiveValidator() {
_onlyActiveValidator();
_;
}
function initialize(uint256 _votingThresholdPercentage) external initializer {
votingThresholdPercentage = _votingThresholdPercentage;
}
function setDependencies(address _contractsRegistryAddr, bytes memory) public override dependant {
IContractsRegistry contractsRegistry = IContractsRegistry(_contractsRegistryAddr);
_contractsRegistry = contractsRegistry;
_staking = IStaking(contractsRegistry.getStakingContract());
_dkg = IDKG(contractsRegistry.getDKGContract());
}
// solhint-disable-next-line ordering
function setVotingThresholdPercentage(uint256 _votingThresholdPercentage) external override onlySigner {
votingThresholdPercentage = _votingThresholdPercentage;
}
function createProposal(
address _validatorAddr,
string calldata _reason
) external override onlyActiveValidator returns (uint256) {
require(_dkg.isActiveValidator(_validatorAddr), "SlashingVoting: Target is not an active validator");
require(!hasPendingSlashingProposal(_validatorAddr), "SlashingVoting: Validator already has pending proposal");
uint256 newProposalId = ++lastProposalId;
SlashingProposalData storage proposalData = _proposalsData[newProposalId];
proposalData.validator = _validatorAddr;
proposalData.reason = _reason;
proposalData.epochId = _dkg.getActiveEpochId();
proposalData.votingStartTime = block.timestamp;
proposalData.votingEndTime = _dkg.createProposal();
_pendingSlashingProposals[_validatorAddr] = newProposalId;
_vote(newProposalId);
emit ProposalCreated(newProposalId, _validatorAddr);
return newProposalId;
}
function vote(uint256 _proposalId) external override onlyActiveValidator {
_vote(_proposalId);
}
function getDetailedProposalInfo(uint256 _proposalId) external view override returns (DetailedProposalInfo memory) {
SlashingProposalData storage proposalData = _proposalsData[_proposalId];
return
DetailedProposalInfo(
getBaseProposalInfo(_proposalId),
proposalData.isExecuted,
proposalData.votedValidatorsSet.values()
);
}
function getBaseProposalInfo(uint256 _proposalId) public view override returns (BaseProposalInfo memory) {
SlashingProposalData storage proposalData = _proposalsData[_proposalId];
return
BaseProposalInfo(
proposalData.validator,
proposalData.reason,
proposalData.epochId,
proposalData.votingStartTime,
proposalData.votingEndTime
);
}
function isProposalExecuted(uint256 _proposalId) public view override returns (bool) {
return _proposalsData[_proposalId].isExecuted;
}
function hasPendingSlashingProposal(address _userAddr) public view override returns (bool) {
uint256 pendingProposalId = _pendingSlashingProposals[_userAddr];
return pendingProposalId != 0 && _proposalsData[pendingProposalId].votingEndTime > block.timestamp;
}
function getValidatorsPercentage(uint256 _currentVotes) public view override returns (uint256) {
return (_currentVotes * PERCENTAGE_100) / _dkg.getActiveValidatorsCount();
}
function _vote(uint256 _proposalId) internal {
SlashingProposalData storage proposalData = _proposalsData[_proposalId];
require(proposalData.votingStartTime > 0, "SlashingVoting: Proposal does not exist");
require(!proposalData.isExecuted, "SlashingVoting: Proposal has already executed");
require(block.timestamp < proposalData.votingEndTime, "SlashingVoting: Voting is already over");
require(proposalData.votedValidatorsSet.add(msg.sender), "SlashingVoting: Validator has already voted");
if (getValidatorsPercentage(proposalData.votedValidatorsSet.length()) >= votingThresholdPercentage) {
address validatorAddr = proposalData.validator;
_dkg.slashValidator(validatorAddr);
_staking.slashValidator(validatorAddr);
proposalData.isExecuted = true;
delete _pendingSlashingProposals[validatorAddr];
emit ProposalExecuted(_proposalId, validatorAddr);
}
emit ProposalVoted(_proposalId, msg.sender);
}
function _onlySigner() internal view {
require(_contractsRegistry.getSigner() == msg.sender, "SlashingVoting: Not a signer");
}
function _onlyActiveValidator() internal view {
require(_dkg.isActiveValidator(msg.sender), "SlashingVoting: Not an active system validator");
}
}