Contract Address Details

0x1000000000000000000000000000000000000000

Contract Name
ValidatorSetHbbft
Creator
Balance
0 tDMD
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
126768
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
ValidatorSetHbbft




Optimization enabled
true
Compiler version
v0.8.25+commit.b61c2a91




Optimization runs
800
Verified at
2024-12-09 15:25:43.030568Z

contracts/ValidatorSetHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { IKeyGenHistory } from "./interfaces/IKeyGenHistory.sol";
import { IRandomHbbft } from "./interfaces/IRandomHbbft.sol";
import { IStakingHbbft } from "./interfaces/IStakingHbbft.sol";
import { IValidatorSetHbbft } from "./interfaces/IValidatorSetHbbft.sol";
import { IBonusScoreSystem } from "./interfaces/IBonusScoreSystem.sol";
import { Unauthorized, ValidatorsListEmpty, ZeroAddress } from "./lib/Errors.sol";
/// @dev Stores the current validator set and contains the logic for choosing new validators
/// before each staking epoch. The logic uses a random seed generated and stored by the `RandomHbbft` contract.
contract ValidatorSetHbbft is Initializable, OwnableUpgradeable, IValidatorSetHbbft {
// =============================================== Storage ========================================================
// WARNING: since this contract is upgradeable, do not remove
// existing storage variables and do not change their types!
address[] internal _currentValidators;
address[] internal _pendingValidators;
address[] internal _previousValidators;
/// @dev The address of the `BlockRewardHbbft` contract.
address public blockRewardContract;
/// @dev A boolean flag indicating whether the specified mining address is in the current validator set.
/// See the `getValidators` getter.
mapping(address => bool) public isValidator;
/// @dev A boolean flag indicating whether the specified mining address was a validator in the previous set.
/// See the `getPreviousValidators` getter.
mapping(address => bool) public isValidatorPrevious;
/// @dev A mining address bound to a specified staking address.
/// See the `_setStakingAddress` internal function.
mapping(address => address) public miningByStakingAddress;
/// @dev The `RandomHbbft` contract address.
address public randomContract;
/// @dev A staking address bound to a specified mining address.
/// See the `_setStakingAddress` internal function.
mapping(address => address) public stakingByMiningAddress;
/// @dev The `StakingHbbft` contract address.
IStakingHbbft public stakingContract;
/// @dev The `KeyGenHistory` contract address.
IKeyGenHistory public keyGenHistoryContract;
/// @dev How many times the given mining address has become a validator.
mapping(address => uint256) public validatorCounter;
/// @dev holds timestamps of last changes in `validatorAvailableSince`
mapping(address => uint256) public validatorAvailableSinceLastWrite;
/// @dev holds Availability information for each specific mining address
/// unavailability happens if a validator gets voted to become a pending validator,
/// but misses out the sending of the ACK or PART within the given timeframe.
/// validators are required to declare availability,
/// in order to become available for voting again.
/// the value is of type timestamp
mapping(address => uint256) public validatorAvailableSince;
/// @dev The max number of validators.
uint256 public maxValidators;
/// @dev time in seconds after which the inactive validator is considered abandoned
uint256 public validatorInactivityThreshold;
IBonusScoreSystem public bonusScoreSystem;
address public connectivityTracker;
// ================================================ Events ========================================================
event ValidatorAvailable(address validator, uint256 timestamp);
/// @dev Emitted by the `handleFailedKeyGeneration` and `notifyUnavailability` functions to signal that a specific
/// validator was marked as unavailable since he dit not contribute to the required key shares or 2/3 of other
/// validators reporter him as disconnected.
event ValidatorUnavailable(address validator, uint256 timestamp);
error AnnounceBlockNumberTooOld();
error CantAnnounceAvailability();
error EpochNotYetzFinished();
error InitialAddressesLengthMismatch();
error InitialValidatorsEmpty();
error InvalidAddressPair();
error InvalidAnnounceBlockNumber();
error InvalidAnnounceBlockHash();
error InvalidInactivityThreshold();
error InvalidPossibleValidatorCount();
error MiningAddressAlreadyUsed(address _value);
error StakingAddressAlreadyUsed(address _value);
error StakingPoolNotExist(address _mining);
// ============================================== Modifiers =======================================================
/// @dev Ensures the caller is the BlockRewardHbbft contract address.
modifier onlyBlockRewardContract() {
if (msg.sender != blockRewardContract) {
revert Unauthorized();
}
_;
}
/// @dev Ensures the caller is the StakingHbbft contract address.
modifier onlyStakingContract() {
if (msg.sender != address(stakingContract)) {
revert Unauthorized();
}
_;
}
modifier onlyConnectivityTracker() {
if (msg.sender != connectivityTracker) {
revert Unauthorized();
}
_;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
// Prevents initialization of implementation contract
_disableInitializers();
}
// =============================================== Setters ========================================================
/// @dev Initializes the network parameters. Used by the
/// constructor of the `InitializerHbbft` contract.
/// @param _contractOwner The address of the contract owner.
/// @param _params ValidatorSetHbbft contract parameeters (introduced to avoid stack too deep issue):
/// blockRewardContract The address of the `BlockRewardHbbft` contract.
/// randomContract The address of the `RandomHbbft` contract.
/// stakingContract The address of the `StakingHbbft` contract.
/// keyGenHistoryContract The address of the `KeyGenHistory` contract.
/// bonusScoreContract The address of the `BonusScoreSystem` contract.
/// validatorInactivityThreshold The time of inactivity in seconds to consider validator abandoned
/// @param _initialMiningAddresses The array of initial validators' mining addresses.
/// @param _initialStakingAddresses The array of initial validators' staking addresses.
function initialize(
address _contractOwner,
ValidatorSetParams calldata _params,
address[] calldata _initialMiningAddresses,
address[] calldata _initialStakingAddresses
) external initializer {
_validateParams(_params);
if (_contractOwner == address(0)) {
revert ZeroAddress();
}
if (_initialMiningAddresses.length == 0) {
revert ValidatorsListEmpty();
}
if (_initialMiningAddresses.length != _initialStakingAddresses.length) {
revert InitialAddressesLengthMismatch();
}
__Ownable_init(_contractOwner);
blockRewardContract = _params.blockRewardContract;
randomContract = _params.randomContract;
stakingContract = IStakingHbbft(_params.stakingContract);
keyGenHistoryContract = IKeyGenHistory(_params.keyGenHistoryContract);
bonusScoreSystem = IBonusScoreSystem(_params.bonusScoreContract);
connectivityTracker = _params.connectivityTrackerContract;
validatorInactivityThreshold = _params.validatorInactivityThreshold;
// Add initial validators to the `_currentValidators` array
for (uint256 i = 0; i < _initialMiningAddresses.length; i++) {
address miningAddress = _initialMiningAddresses[i];
_currentValidators.push(miningAddress);
// _pendingValidators.push(miningAddress);
isValidator[miningAddress] = true;
validatorCounter[miningAddress]++;
_setStakingAddress(miningAddress, _initialStakingAddresses[i]);
}
maxValidators = 25;
}
/// @dev Called by the system when a pending validator set is ready to be activated.
/// After this function is called, the `getValidators` getter returns the new validator set.
/// If this function finalizes, a new validator set is created by the `newValidatorSet` function.
/// an old validator set is also stored and can be read by the `getPreviousValidators` getter.
function finalizeChange() external onlyBlockRewardContract {
if (_pendingValidators.length != 0) {
// Apply a new validator set formed by the `newValidatorSet` function
_savePreviousValidators();
_finalizeNewValidators();
}
_rewardValidatorsStandBy();
_penaliseValidatorsNoStandBy();
// new epoch starts
stakingContract.incrementStakingEpoch();
keyGenHistoryContract.notifyNewEpoch();
delete _pendingValidators;
stakingContract.setStakingEpochStartTime(block.timestamp);
}
/// @dev Implements the logic which forms a new validator set. If the number of active pools
/// is greater than maxValidators, the logic chooses the validators randomly using a random seed generated and
/// stored by the `RandomHbbft` contract.
/// Automatically called by the `BlockRewardHbbft.reward` function at the latest block of the staking epoch.
function newValidatorSet() external onlyBlockRewardContract {
_newValidatorSet(new address[](0));
}
/// @dev called by validators when a validator comes online after
/// getting marked as unavailable caused by a failed key generation.
function announceAvailability(uint256 _blockNumber, bytes32 _blockhash) external {
if (!canCallAnnounceAvailability(msg.sender)) {
revert CantAnnounceAvailability();
}
if (_blockNumber >= block.number) {
revert InvalidAnnounceBlockNumber();
}
// 255 is a technical limitation of EVM ability to look into the past.
// however, we query just for 16 blocks here.
// this provides a time window big enough for valid nodes.
if (_blockNumber + 16 <= block.number) {
revert AnnounceBlockNumberTooOld();
}
// we have ensured now that we technicaly able to query the blockhash for that block
if (blockhash(_blockNumber) != _blockhash) {
revert InvalidAnnounceBlockHash();
}
uint256 timestamp = block.timestamp;
_writeValidatorAvailableSince(msg.sender, timestamp);
stakingContract.notifyAvailability(stakingByMiningAddress[msg.sender]);
emit ValidatorAvailable(msg.sender, timestamp);
}
/// @dev called by blockreward contract when a the reward when the block reward contract
/// came to the conclusion that the validators could not manage to create a new shared key together.
/// this starts the process to find replacements for the failing candites,
/// as well as marking them unavailable.
function handleFailedKeyGeneration() external onlyBlockRewardContract {
// we should only kick out nodes if the nodes have been really to late.
if (block.timestamp < stakingContract.stakingFixedEpochEndTime()) {
revert EpochNotYetzFinished();
}
if (stakingContract.getPoolsToBeElected().length == 0) {
// if there is currently noone able to be elected, we just wait.
// probably this happens, until there is someone manages to make
// his pool available for staking again.
return;
}
if (_pendingValidators.length == 0) {
// if there are no "pending validators" that
// should write their keys, then there is
// nothing to do here.
return;
}
// check if the current epoch should have been ended already
// but some of the validators failed to write his PARTS / ACKS.
// there are 2 scenarious:
// 1.) missing Part: one or more validator was chosen, but never wrote his PART (most likely)
// 2.) missing ACK: all validators were able to write their parts, but one or more failed to write
// it's part.
//
// ad missing Part:
// in this case we can just replace the validator with another one,
// or if there is no other one available, continue with a smaller set.
// ad missing ACK:
// this case is more complex, since nodes did already write their acks for the parts
// of a node that now drops out.
// this should be a very rare case, and to make it simple,
// we can just start over with the random selection of validators again.
// temporary array to keep track of the good validators.
// not all storage slots might be used.
// we asume that there is at minimum 1 bad validator.
address[] memory goodValidators = new address[](_pendingValidators.length - 1);
uint256 goodValidatorsCount = 0;
(uint128 numberOfPartsWritten, uint128 numberOfAcksWritten) = keyGenHistoryContract
.getNumberOfKeyFragmentsWritten();
//address[] memory badValidators = new address[];
for (uint256 i = 0; i < _pendingValidators.length; i++) {
// get mining address for this pool.
// if the mining address did his job (writing PART or ACKS).
// add it to the good pool.
address miningAddress = _pendingValidators[i]; //miningByStakingAddress[];
// if a validator is good or bad, depends if he managed
// the write the information required for the current state.
bool isGood = false;
if (_pendingValidators.length > numberOfPartsWritten) {
// case 1: missing part scenario.
// pending validator that missed out writing their part ?
// maybe make a more precise length check in the future here ?
isGood = keyGenHistoryContract.getPart(miningAddress).length > 0;
} else if (_pendingValidators.length > numberOfAcksWritten) {
// case 2: parts were written, but did this validator also write it's ACKS ??
// Note: we do not really need to check if the validator has written his part,
// since all validators managed to write it's part.
isGood = keyGenHistoryContract.getAcksLength(miningAddress) > 0;
}
if (isGood) {
// we track all good validators,
// so we can later pass the good validators
// to the _newValidatorSet function.
goodValidators[goodValidatorsCount] = _pendingValidators[i];
goodValidatorsCount++;
} else {
// this Pool is not available anymore.
// the pool does not get a Ban,
// but is treated as "inactive" as long it does not `announceAvailability()`
// Decrease validator bonus score because of missed Part/ACK
// Should be called before `removePool` as it's changes pool likelihood
bonusScoreSystem.penaliseNoKeyWrite(miningAddress);
stakingContract.removePool(stakingByMiningAddress[miningAddress]);
// mark the Node address as not available.
_writeValidatorAvailableSince(miningAddress, 0);
emit ValidatorUnavailable(miningAddress, block.timestamp);
}
}
keyGenHistoryContract.clearPrevKeyGenState(_pendingValidators);
keyGenHistoryContract.notifyKeyGenFailed();
// we might only set a subset to the newValidatorSet function,
// since the last indexes of the array are holding unused slots.
address[] memory forcedPools = new address[](goodValidatorsCount);
for (uint256 i = 0; i < goodValidatorsCount; i++) {
forcedPools[i] = goodValidators[i];
}
// this tells the staking contract that the key generation failed
// so the staking conract is able to prolong this staking period.
stakingContract.notifyKeyGenFailed();
// is there anyone left that can get elected ??
// if not, we just continue with the validator set we have now,
// for another round,
// hopefully that on or the other node operators get his pool fixed.
// the Deadline just stays a full time window.
// therefore the Node Operators might get a chance that
// many manage to fix the problem,
// and we can get a big takeover.
if (stakingContract.getPoolsToBeElected().length > 0) {
_newValidatorSet(forcedPools);
}
}
/// @dev Notifies hbbft validator set contract that a validator
/// asociated with the given `_stakingAddress` became
/// unavailable and must be flagged as unavailable.
/// @param _miningAddress The address of the validator which became unavailable.
function notifyUnavailability(address _miningAddress) external onlyConnectivityTracker {
stakingContract.removePool(stakingByMiningAddress[_miningAddress]);
_writeValidatorAvailableSince(_miningAddress, 0);
emit ValidatorUnavailable(_miningAddress, block.timestamp);
}
/// @dev Binds a mining address to the specified staking address. Called by the `StakingHbbft.addPool` function
/// when a user wants to become a candidate and creates a pool.
/// See also the `miningByStakingAddress` and `stakingByMiningAddress` public mappings.
/// @param _miningAddress The mining address of the newly created pool. Cannot be equal to the `_stakingAddress`
/// and should never be used as a pool before.
/// @param _stakingAddress The staking address of the newly created pool. Cannot be equal to the `_miningAddress`
/// and should never be used as a pool before.
function setStakingAddress(address _miningAddress, address _stakingAddress) external onlyStakingContract {
_setStakingAddress(_miningAddress, _stakingAddress);
}
/// @dev set's the validators ip address.
/// this function can only be called by validators.
/// @param _ip IPV4 address of a running Node Software or Proxy.
/// @param _port port for IPv4 address of a running Node Software or Proxy.
function setValidatorInternetAddress(bytes16 _ip, bytes2 _port) external {
// get stacking address of sender. (required)
address validatorAddress = stakingByMiningAddress[msg.sender];
if (validatorAddress == address(0)) {
revert StakingPoolNotExist(msg.sender);
}
// optional: we could verify public key to signer (public key) integrity, but it is costly.
stakingContract.setValidatorInternetAddress(validatorAddress, _ip, _port);
}
// =============================================== Getters ========================================================
function getStakingContract() external view returns (address) {
return address(stakingContract);
}
/// @dev only a network with the maximum number of validators is considered to be at full health.
/// this is especially true for the use of the generated random numbers.
function isFullHealth() external view virtual returns (bool) {
return maxValidators == _currentValidators.length;
}
function getCurrentValidatorsCount() external view returns (uint256) {
return _currentValidators.length;
}
/// @dev Returns the previous validator set (validators' mining addresses array).
/// The array is stored by the `finalizeChange` function
/// when a new staking epoch's validator set is finalized.
function getPreviousValidators() external view returns (address[] memory) {
return _previousValidators;
}
/// @dev Returns the current array of pending validators i.e. waiting to be activated in the new epoch
/// The pending array is changed when a validator is removed as malicious
/// or the validator set is updated by the `newValidatorSet` function.
function getPendingValidators() external view returns (address[] memory) {
return _pendingValidators;
}
/// @dev Returns the current validator set (an array of mining addresses)
/// which always matches the validator set kept in validator's node.
function getValidators() external view returns (address[] memory) {
return _currentValidators;
}
function getPendingValidatorKeyGenerationMode(address _miningAddress) external view returns (KeyGenMode) {
// enum KeyGenMode { NotAPendingValidator, WritePart, WaitForOtherParts,
// WriteAck, WaitForOtherAcks, AllKeysDone }
if (!isPendingValidator(_miningAddress)) {
return KeyGenMode.NotAPendingValidator;
}
// since we got a part, maybe to validator is about to write his ack ?
// he is allowed to write his ack, if all nodes have written their part.
(uint128 numberOfPartsWritten, uint128 numberOfAcksWritten) = keyGenHistoryContract
.getNumberOfKeyFragmentsWritten();
if (numberOfPartsWritten < _pendingValidators.length) {
bytes memory part = keyGenHistoryContract.getPart(_miningAddress);
if (part.length == 0) {
// we know here that the validator is pending,
// but dit not have written the part yet.
// so he is allowed to write it's part.
return KeyGenMode.WritePart;
} else {
// this mining address has written their part.
return KeyGenMode.WaitForOtherParts;
}
} else if (numberOfAcksWritten < _pendingValidators.length) {
// not all Acks Written, so the key is not complete.
// we know know that all Nodes have written their PART.
// but not all have written their ACK.
// are we the one who has written his ACK.
if (keyGenHistoryContract.getAcksLength(_miningAddress) == 0) {
return KeyGenMode.WriteAck;
} else {
return KeyGenMode.WaitForOtherAcks;
}
} else {
return KeyGenMode.AllKeysDone;
}
}
/// @dev Returns a boolean flag indicating whether the specified mining address is a validator
/// or is in the `_pendingValidators`.
/// Used by the `StakingHbbft.maxWithdrawAllowed` and `StakingHbbft.maxWithdrawOrderAllowed` getters.
/// @param _miningAddress The mining address.
function isValidatorOrPending(address _miningAddress) external view returns (bool) {
return isValidator[_miningAddress] || isPendingValidator(_miningAddress);
}
/// @dev Returns a boolean flag indicating whether the specified mining address is a pending validator.
/// Used by the `isValidatorOrPending` and `KeyGenHistory.writeAck/Part` functions.
/// @param _miningAddress The mining address.
function isPendingValidator(address _miningAddress) public view returns (bool) {
uint256 length = _pendingValidators.length;
for (uint256 i = 0; i < length; ++i) {
if (_miningAddress == _pendingValidators[i]) {
return true;
}
}
return false;
}
/// @dev Returns if the specified _miningAddress is able to announce availability.
/// @param _miningAddress mining address that is allowed/disallowed.
function canCallAnnounceAvailability(address _miningAddress) public view returns (bool) {
if (stakingByMiningAddress[_miningAddress] == address(0)) {
// not a validator node.
return false;
}
if (validatorAvailableSince[_miningAddress] != 0) {
// "Validator was not marked as unavailable."
return false;
}
return true;
}
/// @dev Returns the public key for the given stakingAddress
/// @param _stakingAddress staking address of the wanted public key.
/// @return public key of the _stakingAddress
function publicKeyByStakingAddress(address _stakingAddress) external view returns (bytes memory) {
return stakingContract.getPoolPublicKey(_stakingAddress);
}
/// @dev Returns a boolean flag indicating whether the specified validator unavailable
/// for `validatorInactivityThreshold` seconds
/// @param _stakingAddress staking pool address.
function isValidatorAbandoned(address _stakingAddress) external view returns (bool) {
address validator = miningByStakingAddress[_stakingAddress];
if (validatorAvailableSince[validator] != 0) {
return false;
}
uint256 inactiveSeconds = block.timestamp - validatorAvailableSinceLastWrite[validator];
return inactiveSeconds >= validatorInactivityThreshold;
}
/// @dev Returns the public key for the given miningAddress
/// @param _miningAddress mining address of the wanted public key.
/// @return public key of the _miningAddress
function getPublicKey(address _miningAddress) external view returns (bytes memory) {
return stakingContract.getPoolPublicKey(stakingByMiningAddress[_miningAddress]);
}
/// @dev in Hbbft there are sweet spots for the choice of validator counts
/// those are FLOOR((n - 1)/3) * 3 + 1
/// values: 1 - 4 - 7 - 10 - 13 - 16 - 19 - 22 - 25
/// more about: https://github.com/DMDcoin/hbbft-posdao-contracts/issues/84
/// @return a sweet spot n for a given number n
function getValidatorCountSweetSpot(uint256 _possibleValidatorCount) public pure returns (uint256) {
if (_possibleValidatorCount == 0) {
revert InvalidPossibleValidatorCount();
}
if (_possibleValidatorCount < 4) {
return _possibleValidatorCount;
}
return ((_possibleValidatorCount - 1) / 3) * 3 + 1;
}
// ============================================== Internal ========================================================
function _newValidatorSet(address[] memory _forcedPools) internal {
address[] memory poolsToBeElected = stakingContract.getPoolsToBeElected();
uint256 numOfValidatorsToBeElected = poolsToBeElected.length >= maxValidators || poolsToBeElected.length == 0
? maxValidators
: getValidatorCountSweetSpot(poolsToBeElected.length);
// Choose new validators > )
if (poolsToBeElected.length > numOfValidatorsToBeElected) {
uint256 poolsToBeElectedLength = poolsToBeElected.length;
(uint256[] memory likelihood, uint256 likelihoodSum) = stakingContract.getPoolsLikelihood();
address[] memory newValidators = new address[](numOfValidatorsToBeElected);
uint256 indexNewValidator = 0;
for (uint256 iForced = 0; iForced < _forcedPools.length; iForced++) {
for (uint256 iPoolToBeElected = 0; iPoolToBeElected < poolsToBeElectedLength; iPoolToBeElected++) {
if (poolsToBeElected[iPoolToBeElected] == _forcedPools[iForced]) {
newValidators[indexNewValidator] = _forcedPools[iForced];
indexNewValidator++;
likelihoodSum -= likelihood[iPoolToBeElected];
// kicking out this pools from the "to be elected" list,
// by replacing it with the last element,
// and virtually reducing it's size.
poolsToBeElectedLength--;
poolsToBeElected[iPoolToBeElected] = poolsToBeElected[poolsToBeElectedLength];
likelihood[iPoolToBeElected] = likelihood[poolsToBeElectedLength];
break;
}
}
}
uint256 randomNumber = IRandomHbbft(randomContract).currentSeed();
if (likelihood.length > 0 && likelihoodSum > 0) {
for (uint256 i = 0; i < newValidators.length; i++) {
randomNumber = uint256(keccak256(abi.encode(randomNumber ^ block.timestamp)));
uint256 randomPoolIndex = _getRandomIndex(likelihood, likelihoodSum, randomNumber);
newValidators[i] = poolsToBeElected[randomPoolIndex];
likelihoodSum -= likelihood[randomPoolIndex];
poolsToBeElectedLength--;
poolsToBeElected[randomPoolIndex] = poolsToBeElected[poolsToBeElectedLength];
likelihood[randomPoolIndex] = likelihood[poolsToBeElectedLength];
}
_setPendingValidators(newValidators);
}
} else {
//note: it is assumed here that _forcedPools is always a subset of poolsToBeElected.
// a forcedPool can never get picked up if it is not part of the poolsToBeElected.
// the logic needs to be that consistent.
_setPendingValidators(poolsToBeElected);
}
// clear previousValidator KeyGenHistory state
keyGenHistoryContract.clearPrevKeyGenState(_currentValidators);
if (poolsToBeElected.length != 0) {
// Remove pools marked as `to be removed`
stakingContract.removePools();
}
// a new validator set can get choosen already outside the timeframe for phase 2.
// this can happen if the network got stuck and get's repaired.
// and the repair takes longer than a single epoch.
// we detect this case here and grant an extra time window
// so the selected nodes also get their chance to write their keys.
// more about: https://github.com/DMDcoin/hbbft-posdao-contracts/issues/96
// timescale:
// epoch start time ..... phase 2 transition .... current end of phase 2 ..... now ..... new end of phase 2.
// new extra window size has to cover the difference between phase2 transition and now.
// to reach the new end of phase 2.
// current end of phase 2 : stakingContract.stakingFixedEpochEndTime()
// now: block.timestamp
if (block.timestamp > stakingContract.stakingFixedEpochEndTime()) {
stakingContract.notifyNetworkOfftimeDetected(block.timestamp - stakingContract.stakingFixedEpochEndTime());
}
}
/// @dev Sets a new validator set stored in `_pendingValidators` array.
/// Called by the `finalizeChange` function.
function _finalizeNewValidators() internal {
address[] memory validators;
uint256 i;
validators = _currentValidators;
for (i = 0; i < validators.length; i++) {
isValidator[validators[i]] = false;
}
_currentValidators = _pendingValidators;
validators = _currentValidators;
for (i = 0; i < validators.length; i++) {
address miningAddress = validators[i];
isValidator[miningAddress] = true;
validatorCounter[miningAddress]++;
}
}
/// @dev Stores previous validators. Used by the `finalizeChange` function.
function _savePreviousValidators() internal {
uint256 length;
uint256 i;
// Save the previous validator set
length = _previousValidators.length;
for (i = 0; i < length; i++) {
isValidatorPrevious[_previousValidators[i]] = false;
}
length = _currentValidators.length;
for (i = 0; i < length; i++) {
isValidatorPrevious[_currentValidators[i]] = true;
}
_previousValidators = _currentValidators;
}
/// @dev Sets a new validator set as a pending.
/// Called by the `newValidatorSet` function.
/// @param _stakingAddresses The array of the new validators' staking addresses.
function _setPendingValidators(address[] memory _stakingAddresses) internal {
// clear the pending validators list first
delete _pendingValidators;
if (_stakingAddresses.length == 0) {
// If there are no `poolsToBeElected`, we remove the
// validators which want to exit from the validator set
uint256 curValidatorsLength = _currentValidators.length;
for (uint256 i = 0; i < curValidatorsLength; ++i) {
address pvMiningAddress = _currentValidators[i];
address pvStakingAddress = stakingByMiningAddress[pvMiningAddress];
if (
stakingContract.isPoolActive(pvStakingAddress) &&
stakingContract.orderedWithdrawAmount(pvStakingAddress, pvStakingAddress) == 0
) {
// The validator has an active pool and is not going to withdraw their
// entire stake, so this validator doesn't want to exit from the validator set
_pendingValidators.push(pvMiningAddress);
}
}
if (_pendingValidators.length == 0) {
_pendingValidators.push(_currentValidators[0]); // add at least on validator
}
} else {
uint256 stakingAddresseLength = _stakingAddresses.length;
for (uint256 i = 0; i < stakingAddresseLength; ++i) {
_pendingValidators.push(miningByStakingAddress[_stakingAddresses[i]]);
}
}
}
/// @dev Binds a mining address to the specified staking address. Used by the `setStakingAddress` function.
/// See also the `miningByStakingAddress` and `stakingByMiningAddress` public mappings.
/// @param _miningAddress The mining address of the newly created pool. Cannot be equal to the `_stakingAddress`
/// and should never be used as a pool before.
/// @param _stakingAddress The staking address of the newly created pool. Cannot be equal to the `_miningAddress`
/// and should never be used as a pool before.
function _setStakingAddress(address _miningAddress, address _stakingAddress) internal {
if (_miningAddress == address(0)) {
revert ZeroAddress();
}
if (_stakingAddress == address(0)) {
revert ZeroAddress();
}
if (_miningAddress == _stakingAddress) {
revert InvalidAddressPair();
}
if (
miningByStakingAddress[_stakingAddress] != address(0) ||
stakingByMiningAddress[_stakingAddress] != address(0)
) {
revert StakingAddressAlreadyUsed(_stakingAddress);
}
if (
miningByStakingAddress[_miningAddress] != address(0) || stakingByMiningAddress[_miningAddress] != address(0)
) {
revert MiningAddressAlreadyUsed(_miningAddress);
}
miningByStakingAddress[_stakingAddress] = _miningAddress;
stakingByMiningAddress[_miningAddress] = _stakingAddress;
}
/// @dev Writes `validatorAvaialableSince` and saves timestamp of last change.
/// @param _validator validator address
/// @param _availableSince timestamp when the validator became available, 0 if unavailable
function _writeValidatorAvailableSince(address _validator, uint256 _availableSince) internal {
validatorAvailableSince[_validator] = _availableSince;
validatorAvailableSinceLastWrite[_validator] = block.timestamp;
}
function _rewardValidatorsStandBy() internal {
address[] memory poolsToBeElected = stakingContract.getPoolsToBeElected();
uint256 poolsLength = poolsToBeElected.length;
for (uint256 i = 0; i < poolsLength; ++i) {
address mining = miningByStakingAddress[poolsToBeElected[i]];
// slither-disable-next-line incorrect-equality
if (isValidator[mining] || validatorAvailableSince[mining] == 0) {
continue;
}
bonusScoreSystem.rewardStandBy(mining, validatorAvailableSince[mining]);
}
}
function _penaliseValidatorsNoStandBy() internal {
address[] memory poolsInactive = stakingContract.getPoolsInactive();
uint256 poolsLength = poolsInactive.length;
for (uint256 i = 0; i < poolsLength; ++i) {
address mining = miningByStakingAddress[poolsInactive[i]];
if (validatorAvailableSince[mining] != 0) {
continue;
}
bonusScoreSystem.penaliseNoStandBy(mining, validatorAvailableSinceLastWrite[mining]);
}
}
/// @dev Returns an index of a pool in the `poolsToBeElected` array
/// (see the `StakingHbbft.getPoolsToBeElected` public getter)
/// by a random number and the corresponding probability coefficients.
/// Used by the `newValidatorSet` function.
/// @param _likelihood An array of probability coefficients.
/// @param _likelihoodSum A sum of probability coefficients.
/// @param _randomNumber A random number.
function _getRandomIndex(
uint256[] memory _likelihood,
uint256 _likelihoodSum,
uint256 _randomNumber
) internal pure returns (uint256) {
// slither-disable-next-line weak-prng
uint256 random = _randomNumber % _likelihoodSum;
uint256 sum = 0;
uint256 index = 0;
while (sum <= random) {
sum += _likelihood[index];
index++;
}
return index - 1;
}
function _validateParams(ValidatorSetParams calldata _params) private pure {
if (
_params.blockRewardContract == address(0) ||
_params.randomContract == address(0) ||
_params.stakingContract == address(0) ||
_params.keyGenHistoryContract == address(0) ||
_params.bonusScoreContract == address(0)
) {
revert ZeroAddress();
}
}
}

@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}

@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}

@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}

contracts/interfaces/IBonusScoreSystem.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
enum ScoringFactor {
StandByBonus,
NoStandByPenalty,
NoKeyWritePenalty,
BadPerformancePenalty
}
interface IBonusScoreSystem {
function getValidatorScore(address mining) external view returns (uint256);
function rewardStandBy(address mining, uint256 time) external;
function penaliseNoStandBy(address mining, uint256 time) external;
function penaliseNoKeyWrite(address mining) external;
function penaliseBadPerformance(address mining, uint256 time) external;
}

contracts/interfaces/IKeyGenHistory.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
interface IKeyGenHistory {
function clearPrevKeyGenState(address[] calldata) external;
function getAcksLength(address val) external view returns (uint256);
function getPart(address val) external view returns (bytes memory);
function getCurrentKeyGenRound() external view returns (uint256);
function getNumberOfKeyFragmentsWritten()
external
view
returns (uint128, uint128);
function notifyNewEpoch() external;
function notifyKeyGenFailed() external;
}

contracts/interfaces/IRandomHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
interface IRandomHbbft {
function currentSeed() external view returns (uint256);
function getSeedHistoric(uint256 _blocknumber)
external
view
returns (uint256);
function getSeedsHistoric(uint256[] calldata)
external
view
returns (uint256[] memory);
function isFullHealth() external view returns (bool);
function isFullHealthHistoric(uint256)
external
view
returns (bool);
function isFullHealthsHistoric(uint256[] calldata)
external
view
returns (bool[] memory);
}

contracts/interfaces/IStakingHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
interface IStakingHbbft {
struct PoolRewardShares {
uint256 validatorShare;
uint256 nodeOperatorShare;
uint256 delegatorsShare;
}
struct StakingParams {
address _validatorSetContract;
address _bonusScoreContract;
address[] _initialStakingAddresses;
uint256 _delegatorMinStake;
uint256 _candidateMinStake;
uint256 _maxStake;
uint256 _stakingFixedEpochDuration;
uint256 _stakingTransitionTimeframeLength;
uint256 _stakingWithdrawDisallowPeriod;
}
function incrementStakingEpoch() external;
function removePool(address) external;
function removePools() external;
function setStakingEpochStartTime(uint256) external;
function notifyKeyGenFailed() external;
function notifyAvailability(address _stakingAddress) external;
function notifyNetworkOfftimeDetected(uint256) external;
function updatePoolLikelihood(address mining, uint256 validatorScore) external;
function getPoolPublicKey(address _poolAddress)
external
view
returns (bytes memory);
function getPoolsLikelihood()
external
view
returns (uint256[] memory, uint256);
function getPoolsToBeElected() external view returns (address[] memory);
function getPoolsToBeRemoved() external view returns (address[] memory);
function getPoolsInactive() external view returns (address[] memory);
function isPoolActive(address) external view returns (bool);
function isPoolValid(address) external view returns (bool);
function MAX_CANDIDATES() external pure returns (uint256); // solhint-disable-line func-name-mixedcase
function orderedWithdrawAmount(address, address)
external
view
returns (uint256);
function poolDelegators(address) external view returns (address[] memory);
function setValidatorInternetAddress(
address,
bytes16,
bytes2
) external;
function stakeAmount(address, address) external view returns (uint256);
function stakeAmountTotal(address) external view returns (uint256);
function totalStakedAmount() external view returns (uint256);
function stakingWithdrawDisallowPeriod() external view returns (uint256);
function stakingEpoch() external view returns (uint256);
function stakingFixedEpochDuration() external view returns (uint256);
function startTimeOfNextPhaseTransition() external view returns (uint256);
function stakingFixedEpochEndTime() external view returns (uint256);
function stakingEpochStartTime() external view returns (uint256);
function stakingEpochStartBlock() external view returns (uint256);
function restake(
address _poolStakingAddress,
uint256 validatorReward
) external payable;
function snapshotPoolStakeAmounts(
uint256 _epoch,
address _stakingPool
) external;
function getPoolValidatorStakeAmount(
uint256 _epoch,
address _stakingPool
) external view returns (uint256);
}

contracts/interfaces/IValidatorSetHbbft.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
interface IValidatorSetHbbft {
struct ValidatorSetParams {
address blockRewardContract;
address randomContract;
address stakingContract;
address keyGenHistoryContract;
address bonusScoreContract;
address connectivityTrackerContract;
uint256 validatorInactivityThreshold;
}
// Key Generation states of validator.
enum KeyGenMode {
NotAPendingValidator,
WritePart,
WaitForOtherParts,
WriteAck,
WaitForOtherAcks,
AllKeysDone
}
function announceAvailability(uint256, bytes32) external;
function finalizeChange() external;
function newValidatorSet() external;
function setStakingAddress(address, address) external;
function handleFailedKeyGeneration() external;
function isFullHealth() external view returns (bool);
function blockRewardContract() external view returns (address);
function canCallAnnounceAvailability(address _miningAddress)
external
view
returns (bool);
function getPendingValidators() external view returns (address[] memory);
function getPreviousValidators() external view returns (address[] memory);
function getValidators() external view returns (address[] memory);
function isValidator(address) external view returns (bool);
function isValidatorOrPending(address) external view returns (bool);
function isPendingValidator(address) external view returns (bool);
function getPendingValidatorKeyGenerationMode(address)
external
view
returns (KeyGenMode);
function maxValidators() external view returns (uint256);
function miningByStakingAddress(address) external view returns (address);
function randomContract() external view returns (address);
function notifyUnavailability(address) external;
function stakingByMiningAddress(address) external view returns (address);
function publicKeyByStakingAddress(address)
external
view
returns (bytes memory);
function getPublicKey(address) external view returns (bytes memory);
function getStakingContract() external view returns (address);
function validatorAvailableSince(address) external view returns (uint256);
function isValidatorAbandoned(address) external view returns (bool);
function getValidatorCountSweetSpot(uint256)
external
view
returns (uint256);
function getCurrentValidatorsCount() external view returns (uint256);
}

contracts/lib/Errors.sol

// SPDX-License-Identifier: Apache 2.0
pragma solidity =0.8.25;
error Unauthorized();
error ValidatorsListEmpty();
error ZeroAddress();
error ZeroGasPrice();

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"error","name":"AnnounceBlockNumberTooOld","inputs":[]},{"type":"error","name":"CantAnnounceAvailability","inputs":[]},{"type":"error","name":"EpochNotYetzFinished","inputs":[]},{"type":"error","name":"InitialAddressesLengthMismatch","inputs":[]},{"type":"error","name":"InitialValidatorsEmpty","inputs":[]},{"type":"error","name":"InvalidAddressPair","inputs":[]},{"type":"error","name":"InvalidAnnounceBlockHash","inputs":[]},{"type":"error","name":"InvalidAnnounceBlockNumber","inputs":[]},{"type":"error","name":"InvalidInactivityThreshold","inputs":[]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"InvalidPossibleValidatorCount","inputs":[]},{"type":"error","name":"MiningAddressAlreadyUsed","inputs":[{"type":"address","name":"_value","internalType":"address"}]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"error","name":"StakingAddressAlreadyUsed","inputs":[{"type":"address","name":"_value","internalType":"address"}]},{"type":"error","name":"StakingPoolNotExist","inputs":[{"type":"address","name":"_mining","internalType":"address"}]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"error","name":"ValidatorsListEmpty","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"event","name":"Initialized","inputs":[{"type":"uint64","name":"version","internalType":"uint64","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ValidatorAvailable","inputs":[{"type":"address","name":"validator","internalType":"address","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ValidatorUnavailable","inputs":[{"type":"address","name":"validator","internalType":"address","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"announceAvailability","inputs":[{"type":"uint256","name":"_blockNumber","internalType":"uint256"},{"type":"bytes32","name":"_blockhash","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"blockRewardContract","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IBonusScoreSystem"}],"name":"bonusScoreSystem","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"canCallAnnounceAvailability","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"connectivityTracker","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"finalizeChange","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentValidatorsCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"enum IValidatorSetHbbft.KeyGenMode"}],"name":"getPendingValidatorKeyGenerationMode","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getPendingValidators","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getPreviousValidators","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"getPublicKey","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getStakingContract","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getValidatorCountSweetSpot","inputs":[{"type":"uint256","name":"_possibleValidatorCount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getValidators","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"handleFailedKeyGeneration","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_contractOwner","internalType":"address"},{"type":"tuple","name":"_params","internalType":"struct IValidatorSetHbbft.ValidatorSetParams","components":[{"type":"address","name":"blockRewardContract","internalType":"address"},{"type":"address","name":"randomContract","internalType":"address"},{"type":"address","name":"stakingContract","internalType":"address"},{"type":"address","name":"keyGenHistoryContract","internalType":"address"},{"type":"address","name":"bonusScoreContract","internalType":"address"},{"type":"address","name":"connectivityTrackerContract","internalType":"address"},{"type":"uint256","name":"validatorInactivityThreshold","internalType":"uint256"}]},{"type":"address[]","name":"_initialMiningAddresses","internalType":"address[]"},{"type":"address[]","name":"_initialStakingAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isFullHealth","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isPendingValidator","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isValidator","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isValidatorAbandoned","inputs":[{"type":"address","name":"_stakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isValidatorOrPending","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isValidatorPrevious","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IKeyGenHistory"}],"name":"keyGenHistoryContract","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxValidators","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"miningByStakingAddress","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"newValidatorSet","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"notifyUnavailability","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"publicKeyByStakingAddress","inputs":[{"type":"address","name":"_stakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"randomContract","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setStakingAddress","inputs":[{"type":"address","name":"_miningAddress","internalType":"address"},{"type":"address","name":"_stakingAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setValidatorInternetAddress","inputs":[{"type":"bytes16","name":"_ip","internalType":"bytes16"},{"type":"bytes2","name":"_port","internalType":"bytes2"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"stakingByMiningAddress","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IStakingHbbft"}],"name":"stakingContract","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"validatorAvailableSince","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"validatorAvailableSinceLastWrite","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"validatorCounter","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"validatorInactivityThreshold","inputs":[]}]
            

Deployed ByteCode

0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6136bb806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102c75760003560e01c80638e68dce41161017b578063b7ab4db5116100d8578063efd049471161008c578063f51726f011610071578063f51726f0146105fa578063facd743b1461060d578063fb64aac11461063057600080fd5b8063efd04947146105d4578063f2fde38b146105e757600080fd5b8063ed7cba62116100bd578063ed7cba6214610599578063ee99205c146105b9578063eebc7a39146105cc57600080fd5b8063b7ab4db51461057e578063ccf69e9b1461058657600080fd5b8063a49904e51161012f578063ade1900a11610114578063ade1900a14610543578063b41832e41461054b578063b47aef1f1461056b57600080fd5b8063a49904e514610514578063a68b5b761461052057600080fd5b80639d6fc1d1116101605780639d6fc1d1146104cb578063a0d16cad146104ee578063a42bdee91461050157600080fd5b80638e68dce4146104a75780639881933d146104b857600080fd5b806360e5c52011610229578063717662ee116101dd57806385602ad5116101c257806385602ad514610442578063857cdbb8146104575780638da5cb5b1461047757600080fd5b8063717662ee14610427578063752862111461043a57600080fd5b8063669554691161020e57806366955469146104035780636b949ae814610416578063715018a61461041f57600080fd5b806360e5c520146103d057806361e61068146103f057600080fd5b80633da74fc61161028057806356b54bae1161026557806356b54bae146103ad5780635ccee1de146103c05780635d5fcbce146103c857600080fd5b80633da74fc61461038757806343bcce9f1461039a57600080fd5b80630a4c1072116102b15780630a4c10721461032957806314e28d901461033e5780631ee4d0bc1461035e57600080fd5b8062535175146102cc57806308ac525614610312575b600080fd5b6102f56102da366004612ff0565b6006602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61031b600e5481565b604051908152602001610309565b61033c610337366004613014565b610643565b005b61035161034c366004612ff0565b61067b565b604051610309919061304d565b6102f561036c366004612ff0565b6008602052600090815260409020546001600160a01b031681565b6010546102f5906001600160a01b031681565b61033c6103a8366004613075565b610864565b6003546102f5906001600160a01b031681565b61033c6109be565b60005461031b565b61031b6103de366004612ff0565b600d6020526000908152604090205481565b61033c6103fe3660046130e3565b610a02565b6011546102f5906001600160a01b031681565b61031b600f5481565b61033c610db4565b61033c610435366004612ff0565b610dc6565b61033c610ecd565b61044a61105c565b6040516103099190613185565b61046a610465366004612ff0565b6110be565b60405161030991906131f6565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03166102f5565b6009546001600160a01b03166102f5565b61046a6104c6366004612ff0565b61114e565b6104de6104d9366004612ff0565b611181565b6040519015158152602001610309565b6104de6104fc366004612ff0565b6111ee565b61033c61050f366004613229565b611219565b600054600e54146104de565b6104de61052e366004612ff0565b60056020526000908152604090205460ff1681565b61033c6112fd565b61031b610559366004612ff0565b600b6020526000908152604090205481565b600a546102f5906001600160a01b031681565b61044a611a98565b6007546102f5906001600160a01b031681565b61031b6105a7366004612ff0565b600c6020526000908152604090205481565b6009546102f5906001600160a01b031681565b61044a611af8565b61031b6105e2366004613292565b611b58565b61033c6105f5366004612ff0565b611bb5565b6104de610608366004612ff0565b611bf3565b6104de61061b366004612ff0565b60046020526000908152604090205460ff1681565b6104de61063e366004612ff0565b611c4a565b6009546001600160a01b0316331461066d576040516282b42960e81b815260040160405180910390fd5b6106778282611ca8565b5050565b600061068682611c4a565b61069257506000919050565b600a5460408051637be02c2b60e01b8152815160009384936001600160a01b0390911692637be02c2b92600480830193928290030181865afa1580156106dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070091906132d0565b60015491935091506fffffffffffffffffffffffffffffffff831610156107b557600a546040516399de1a4360e01b81526001600160a01b03868116600483015260009216906399de1a4390602401600060405180830381865afa15801561076c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610794919081019061334a565b905080516000036107aa57506001949350505050565b506002949350505050565b6001546fffffffffffffffffffffffffffffffff8216101561085a57600a54604051631baeec3f60e31b81526001600160a01b0386811660048301529091169063dd7761f890602401602060405180830381865afa15801561081b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083f91906133de565b600003610850575060039392505050565b5060049392505050565b5060059392505050565b61086d33611bf3565b61088a5760405163a82bfa5560e01b815260040160405180910390fd5b4382106108aa576040516341d852df60e01b815260040160405180910390fd5b436108b683601061340d565b116108d4576040516310548f8360e01b815260040160405180910390fd5b808240146108f55760405163fe1864e160e01b815260040160405180910390fd5b336000908152600d602090815260408083204290819055600c83528184208190556009546008909352818420548251631ec56e5360e21b81526001600160a01b03918216600482015292519194931692637b15b94c926024808201939182900301818387803b15801561096757600080fd5b505af115801561097b573d6000803e3d6000fd5b505060408051338152602081018590527f705d65dbee06de60e6465d68371ce849d5a1ead53e53d01a413b2b01393aa022935001905060405180910390a1505050565b6003546001600160a01b031633146109e8576040516282b42960e81b815260040160405180910390fd5b604080516000815260208101909152610a0090611e46565b565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610a4d5750825b905060008267ffffffffffffffff166001148015610a6a5750303b155b905081158015610a78575080155b15610a965760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610aca57845468ff00000000000000001916680100000000000000001785555b610ad38a61251d565b6001600160a01b038b16610afa5760405163d92e233d60e01b815260040160405180910390fd5b6000889003610b1c576040516322b8b72360e21b815260040160405180910390fd5b878614610b3c5760405163ac9e459360e01b815260040160405180910390fd5b610b458b6125e0565b610b5260208b018b612ff0565b600380546001600160a01b0319166001600160a01b0392909216919091179055610b8260408b0160208c01612ff0565b600780546001600160a01b0319166001600160a01b0392909216919091179055610bb260608b0160408c01612ff0565b600980546001600160a01b0319166001600160a01b0392909216919091179055610be260808b0160608c01612ff0565b600a80546001600160a01b0319166001600160a01b0392909216919091179055610c1260a08b0160808c01612ff0565b601080546001600160a01b0319166001600160a01b0392909216919091179055610c4260c08b0160a08c01612ff0565b601180546001600160a01b0319166001600160a01b039290921691909117905560c08a0135600f5560005b88811015610d565760008a8a83818110610c8957610c89613420565b9050602002016020810190610c9e9190612ff0565b60008054600180820183557f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390910180546001600160a01b0319166001600160a01b0385169081179091558252600460209081526040808420805460ff1916909317909255600b90528120805492935090610d1883613436565b9190505550610d4d818a8a85818110610d3357610d33613420565b9050602002016020810190610d489190612ff0565b611ca8565b50600101610c6d565b506019600e558315610da757845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050505050565b610dbc6125f1565b610a00600061264c565b6011546001600160a01b03163314610df0576040516282b42960e81b815260040160405180910390fd5b6009546001600160a01b0382811660009081526008602052604090819020549051631dbe84a360e11b81529082166004820152911690633b7d094690602401600060405180830381600087803b158015610e4957600080fd5b505af1158015610e5d573d6000803e3d6000fd5b5050506001600160a01b0382166000908152600d60209081526040808320839055600c909152902042905550604080516001600160a01b03831681524260208201527f7ec36d1734626f8b3686f8791130187e1633c843b1995003e788cd2b33d56903910160405180910390a150565b6003546001600160a01b03163314610ef7576040516282b42960e81b815260040160405180910390fd5b60015415610f0f57610f076126bd565b610f0f612794565b610f17612939565b610f1f612abd565b600960009054906101000a90046001600160a01b03166001600160a01b031663ee435f556040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610f6f57600080fd5b505af1158015610f83573d6000803e3d6000fd5b50505050600a60009054906101000a90046001600160a01b03166001600160a01b0316632c6f194d6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610fd757600080fd5b505af1158015610feb573d6000803e3d6000fd5b5050505060016000610ffd9190612f58565b600954604051638247a23960e01b81524260048201526001600160a01b0390911690638247a23990602401600060405180830381600087803b15801561104257600080fd5b505af1158015611056573d6000803e3d6000fd5b50505050565b606060028054806020026020016040519081016040528092919081815260200182805480156110b457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611096575b5050505050905090565b6009546001600160a01b0382811660009081526008602052604090819020549051634e9b426d60e01b815290821660048201526060929190911690634e9b426d906024015b600060405180830381865afa158015611120573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611148919081019061334a565b92915050565b600954604051634e9b426d60e01b81526001600160a01b0383811660048301526060921690634e9b426d90602401611103565b6001600160a01b03808216600090815260066020908152604080832054909316808352600d90915291812054909190156111be5750600092915050565b6001600160a01b0381166000908152600c60205260408120546111e1904261344f565b600f541115949350505050565b6001600160a01b03811660009081526004602052604081205460ff1680611148575061114882611c4a565b336000908152600860205260409020546001600160a01b031680611257576040516303a949c760e61b81523360048201526024015b60405180910390fd5b600954604051637b0a0f9b60e01b81526001600160a01b0383811660048301526fffffffffffffffffffffffffffffffff19861660248301527fffff0000000000000000000000000000000000000000000000000000000000008516604483015290911690637b0a0f9b906064015b600060405180830381600087803b1580156112e057600080fd5b505af11580156112f4573d6000803e3d6000fd5b50505050505050565b6003546001600160a01b03163314611327576040516282b42960e81b815260040160405180910390fd5b600960009054906101000a90046001600160a01b03166001600160a01b03166322e3d9866040518163ffffffff1660e01b8152600401602060405180830381865afa15801561137a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139e91906133de565b4210156113be5760405163112bb3df60e11b815260040160405180910390fd5b600960009054906101000a90046001600160a01b03166001600160a01b031663a5d54f656040518163ffffffff1660e01b8152600401600060405180830381865afa158015611411573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114399190810190613486565b5115610a005760015415610a0057600180546000916114579161344f565b67ffffffffffffffff81111561146f5761146f613303565b604051908082528060200260200182016040528015611498578160200160208202803683370190505b50905060008080600a60009054906101000a90046001600160a01b03166001600160a01b0316637be02c2b6040518163ffffffff1660e01b81526004016040805180830381865afa1580156114f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151591906132d0565b9150915060005b6001548110156118315760006001828154811061153b5761153b613420565b60009182526020822001546001546001600160a01b0390911692506fffffffffffffffffffffffffffffffff861610156115eb57600a546040516399de1a4360e01b81526001600160a01b03848116600483015260009216906399de1a4390602401600060405180830381865afa1580156115ba573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115e2919081019061334a565b5111905061167a565b6001546fffffffffffffffffffffffffffffffff8516101561167a57600a54604051631baeec3f60e31b81526001600160a01b038481166004830152600092169063dd7761f890602401602060405180830381865afa158015611652573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167691906133de565b1190505b80156116ed576001838154811061169357611693613420565b9060005260206000200160009054906101000a90046001600160a01b03168787815181106116c3576116c3613420565b6001600160a01b0390921660209283029190910190910152856116e581613436565b965050611827565b601054604051632bdfe5f760e11b81526001600160a01b038481166004830152909116906357bfcbee90602401600060405180830381600087803b15801561173457600080fd5b505af1158015611748573d6000803e3d6000fd5b50506009546001600160a01b0385811660009081526008602052604090819020549051631dbe84a360e11b8152908216600482015291169250633b7d09469150602401600060405180830381600087803b1580156117a557600080fd5b505af11580156117b9573d6000803e3d6000fd5b5050506001600160a01b0383166000908152600d60209081526040808320839055600c909152902042905550604080516001600160a01b03841681524260208201527f7ec36d1734626f8b3686f8791130187e1633c843b1995003e788cd2b33d56903910160405180910390a15b505060010161151c565b50600a54604051632f8ba4bf60e11b81526001600160a01b0390911690635f17497e906118639060019060040161351a565b600060405180830381600087803b15801561187d57600080fd5b505af1158015611891573d6000803e3d6000fd5b50505050600a60009054906101000a90046001600160a01b03166001600160a01b031663adddc0cf6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156118e557600080fd5b505af11580156118f9573d6000803e3d6000fd5b5050505060008367ffffffffffffffff81111561191857611918613303565b604051908082528060200260200182016040528015611941578160200160208202803683370190505b50905060005b8481101561199b5785818151811061196157611961613420565b602002602001015182828151811061197b5761197b613420565b6001600160a01b0390921660209283029190910190910152600101611947565b50600960009054906101000a90046001600160a01b03166001600160a01b031663adddc0cf6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156119ec57600080fd5b505af1158015611a00573d6000803e3d6000fd5b505050506000600960009054906101000a90046001600160a01b03166001600160a01b031663a5d54f656040518163ffffffff1660e01b8152600401600060405180830381865afa158015611a59573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a819190810190613486565b511115611a9157611a9181611e46565b5050505050565b606060008054806020026020016040519081016040528092919081815260200182805480156110b4576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611096575050505050905090565b606060018054806020026020016040519081016040528092919081815260200182805480156110b4576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611096575050505050905090565b600081600003611b7b5760405163a63671a560e01b815260040160405180910390fd5b6004821015611b88575090565b6003611b9560018461344f565b611b9f9190613574565b611baa906003613588565b61114890600161340d565b611bbd6125f1565b6001600160a01b038116611be757604051631e4fbdf760e01b81526000600482015260240161124e565b611bf08161264c565b50565b6001600160a01b03818116600090815260086020526040812054909116611c1c57506000919050565b6001600160a01b0382166000908152600d602052604090205415611c4257506000919050565b506001919050565b600154600090815b81811015611c9e5760018181548110611c6d57611c6d613420565b6000918252602090912001546001600160a01b0390811690851603611c96575060019392505050565b600101611c52565b5060009392505050565b6001600160a01b038216611ccf5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116611cf65760405163d92e233d60e01b815260040160405180910390fd5b806001600160a01b0316826001600160a01b031603611d28576040516307d9394560e01b815260040160405180910390fd5b6001600160a01b0381811660009081526006602052604090205416151580611d6957506001600160a01b038181166000908152600860205260409020541615155b15611d92576040516326d65e0360e21b81526001600160a01b038216600482015260240161124e565b6001600160a01b0382811660009081526006602052604090205416151580611dd357506001600160a01b038281166000908152600860205260409020541615155b15611dfc5760405163342ed6a360e01b81526001600160a01b038316600482015260240161124e565b6001600160a01b0390811660008181526006602090815260408083208054959096166001600160a01b03199586168117909655948252600890529290922080549091169091179055565b6009546040805163a5d54f6560e01b815290516000926001600160a01b03169163a5d54f6591600480830192869291908290030181865afa158015611e8f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611eb79190810190613486565b90506000600e548251101580611ecc57508151155b611edf57611eda8251611b58565b611ee3565b600e545b9050808251111561231e5781516009546040805163957950a760e01b8152905160009283926001600160a01b039091169163957950a79160048082019286929091908290030181865afa158015611f3e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611f66919081019061359f565b9150915060008467ffffffffffffffff811115611f8557611f85613303565b604051908082528060200260200182016040528015611fae578160200160208202803683370190505b5090506000805b88518110156121335760005b8681101561212a57898281518110611fdb57611fdb613420565b60200260200101516001600160a01b0316898281518110611ffe57611ffe613420565b60200260200101516001600160a01b0316036121225789828151811061202657612026613420565b602002602001015184848151811061204057612040613420565b6001600160a01b03909216602092830291909101909101528261206281613436565b93505085818151811061207757612077613420565b60200260200101518561208a919061344f565b94508661209681613638565b9750508887815181106120ab576120ab613420565b60200260200101518982815181106120c5576120c5613420565b60200260200101906001600160a01b031690816001600160a01b0316815250508587815181106120f7576120f7613420565b602002602001015186828151811061211157612111613420565b60200260200101818152505061212a565b600101611fc1565b50600101611fb5565b5060075460408051634191031360e11b815290516000926001600160a01b03169163832206269160048083019260209291908290030181865afa15801561217e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121a291906133de565b9050600085511180156121b55750600084115b156123135760005b835181101561230957604080514284186020820152016040516020818303038152906040528051906020012060001c915060006121fb878785612c1d565b905089818151811061220f5761220f613420565b602002602001015185838151811061222957612229613420565b60200260200101906001600160a01b031690816001600160a01b03168152505086818151811061225b5761225b613420565b60200260200101518661226e919061344f565b95508761227a81613638565b98505089888151811061228f5761228f613420565b60200260200101518a82815181106122a9576122a9613420565b60200260200101906001600160a01b031690816001600160a01b0316815250508688815181106122db576122db613420565b60200260200101518782815181106122f5576122f5613420565b6020908102919091010152506001016121bd565b5061231383612c86565b505050505050612327565b61232782612c86565b600a54604051632f8ba4bf60e11b81526001600160a01b0390911690635f17497e906123589060009060040161351a565b600060405180830381600087803b15801561237257600080fd5b505af1158015612386573d6000803e3d6000fd5b5050505081516000146123fc57600960009054906101000a90046001600160a01b03166001600160a01b0316631555371c6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156123e357600080fd5b505af11580156123f7573d6000803e3d6000fd5b505050505b600960009054906101000a90046001600160a01b03166001600160a01b03166322e3d9866040518163ffffffff1660e01b8152600401602060405180830381865afa15801561244f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247391906133de565b4211156125185760095460408051631171ecc360e11b815290516001600160a01b0390921691639e72c6359183916322e3d986916004808201926020929091908290030181865afa1580156124cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f091906133de565b6124fa904261344f565b6040518263ffffffff1660e01b81526004016112c691815260200190565b505050565b600061252c6020830183612ff0565b6001600160a01b031614806125595750600061254e6040830160208401612ff0565b6001600160a01b0316145b8061257c575060006125716060830160408401612ff0565b6001600160a01b0316145b8061259f575060006125946080830160608401612ff0565b6001600160a01b0316145b806125c2575060006125b760a0830160808401612ff0565b6001600160a01b0316145b15611bf05760405163d92e233d60e01b815260040160405180910390fd5b6125e8612f02565b611bf081612f50565b336126237f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b031614610a005760405163118cdaa760e01b815233600482015260240161124e565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b60025460005b8181101561272057600060056000600284815481106126e4576126e4613420565b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff19169115159190911790556001016126c3565b505060008054905b818110156127845760016005600080848154811061274857612748613420565b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055600101612728565b6000805461251891600291612f76565b60606000808054806020026020016040519081016040528092919081815260200182805480156127ed57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116127cf575b50505050509150600090505b81518110156128545760006004600084848151811061281a5761281a613420565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff19169115159190911790556001016127f9565b6001805461286491600091612f76565b5060008054806020026020016040519081016040528092919081815260200182805480156128bb57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161289d575b50505050509150600090505b81518110156106775760008282815181106128e4576128e4613420565b6020908102919091018101516001600160a01b0381166000908152600483526040808220805460ff19166001179055600b9093529182208054919350909161292b83613436565b9091555050506001016128c7565b6009546040805163a5d54f6560e01b815290516000926001600160a01b03169163a5d54f6591600480830192869291908290030181865afa158015612982573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526129aa9190810190613486565b805190915060005b81811015612518576000600660008584815181106129d2576129d2613420565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600090812054909316808452600490915291205490915060ff1680612a3257506001600160a01b0381166000908152600d6020526040902054155b15612a3d5750612ab5565b6010546001600160a01b038281166000818152600d6020526040908190205490516392f2944360e01b8152600481019290925260248201529116906392f2944390604401600060405180830381600087803b158015612a9b57600080fd5b505af1158015612aaf573d6000803e3d6000fd5b50505050505b6001016129b2565b6009546040805163df6f55f560e01b815290516000926001600160a01b03169163df6f55f591600480830192869291908290030181865afa158015612b06573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612b2e9190810190613486565b805190915060005b8181101561251857600060066000858481518110612b5657612b56613420565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600090812054909316808452600d90915291205490915015612b9d5750612c15565b6010546001600160a01b038281166000818152600c6020526040908190205490516351150d5760e01b8152600481019290925260248201529116906351150d5790604401600060405180830381600087803b158015612bfb57600080fd5b505af1158015612c0f573d6000803e3d6000fd5b50505050505b600101612b36565b600080612c2a848461364f565b90506000805b828211612c7057868181518110612c4957612c49613420565b602002602001015182612c5c919061340d565b915080612c6881613436565b915050612c30565b612c7b60018261344f565b979650505050505050565b612c9260016000612f58565b8051600003612e865760008054905b81811015612e27576000808281548110612cbd57612cbd613420565b6000918252602080832091909101546001600160a01b03908116808452600890925260409283902054600954935163a711e6a160e01b8152908216600482018190529294509192169063a711e6a190602401602060405180830381865afa158015612d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d509190613663565b8015612dcd575060095460405162e9ab0360e81b81526001600160a01b038381166004830181905260248301529091169063e9ab030090604401602060405180830381865afa158015612da7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dcb91906133de565b155b15612e1d576001805480820182556000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b0384161790555b5050600101612ca1565b5060015460000361067757600160008081548110612e4757612e47613420565b60009182526020808320909101548354600181018555938352912090910180546001600160a01b0319166001600160a01b039092169190911790555050565b805160005b8181101561251857600160066000858481518110612eab57612eab613420565b6020908102919091018101516001600160a01b0390811683528282019390935260409091016000908120548454600181810187559583529290912090910180546001600160a01b0319169190921617905501612e8b565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16610a0057604051631afcd79f60e31b815260040160405180910390fd5b611bbd612f02565b5080546000825590600052602060002090810190611bf09190612fc6565b828054828255906000526020600020908101928215612fb65760005260206000209182015b82811115612fb6578254825591600101919060010190612f9b565b50612fc2929150612fc6565b5090565b5b80821115612fc25760008155600101612fc7565b6001600160a01b0381168114611bf057600080fd5b60006020828403121561300257600080fd5b813561300d81612fdb565b9392505050565b6000806040838503121561302757600080fd5b823561303281612fdb565b9150602083013561304281612fdb565b809150509250929050565b602081016006831061306f57634e487b7160e01b600052602160045260246000fd5b91905290565b6000806040838503121561308857600080fd5b50508035926020909101359150565b60008083601f8401126130a957600080fd5b50813567ffffffffffffffff8111156130c157600080fd5b6020830191508360208260051b85010111156130dc57600080fd5b9250929050565b6000806000806000808688036101408112156130fe57600080fd5b873561310981612fdb565b965060e0601f198201121561311d57600080fd5b5060208701945061010087013567ffffffffffffffff8082111561314057600080fd5b61314c8a838b01613097565b909650945061012089013591508082111561316657600080fd5b5061317389828a01613097565b979a9699509497509295939492505050565b6020808252825182820181905260009190848201906040850190845b818110156131c65783516001600160a01b0316835292840192918401916001016131a1565b50909695505050505050565b60005b838110156131ed5781810151838201526020016131d5565b50506000910152565b60208152600082518060208401526132158160408501602087016131d2565b601f01601f19169190910160400192915050565b6000806040838503121561323c57600080fd5b82356fffffffffffffffffffffffffffffffff198116811461325d57600080fd5b915060208301357fffff0000000000000000000000000000000000000000000000000000000000008116811461304257600080fd5b6000602082840312156132a457600080fd5b5035919050565b80516fffffffffffffffffffffffffffffffff811681146132cb57600080fd5b919050565b600080604083850312156132e357600080fd5b6132ec836132ab565b91506132fa602084016132ab565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561334257613342613303565b604052919050565b60006020828403121561335c57600080fd5b815167ffffffffffffffff8082111561337457600080fd5b818401915084601f83011261338857600080fd5b81518181111561339a5761339a613303565b6133ad601f8201601f1916602001613319565b91508082528560208285010111156133c457600080fd5b6133d58160208401602086016131d2565b50949350505050565b6000602082840312156133f057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115611148576111486133f7565b634e487b7160e01b600052603260045260246000fd5b600060018201613448576134486133f7565b5060010190565b81810381811115611148576111486133f7565b600067ffffffffffffffff82111561347c5761347c613303565b5060051b60200190565b6000602080838503121561349957600080fd5b825167ffffffffffffffff8111156134b057600080fd5b8301601f810185136134c157600080fd5b80516134d46134cf82613462565b613319565b81815260059190911b820183019083810190878311156134f357600080fd5b928401925b82841015612c7b57835161350b81612fdb565b825292840192908401906134f8565b6020808252825482820181905260008481528281209092916040850190845b818110156131c65783546001600160a01b031683526001938401939285019201613539565b634e487b7160e01b600052601260045260246000fd5b6000826135835761358361355e565b500490565b8082028115828204841417611148576111486133f7565b600080604083850312156135b257600080fd5b825167ffffffffffffffff8111156135c957600080fd5b8301601f810185136135da57600080fd5b805160206135ea6134cf83613462565b82815260059290921b8301810191818101908884111561360957600080fd5b938201935b838510156136275784518252938201939082019061360e565b969091015195979596505050505050565b600081613647576136476133f7565b506000190190565b60008261365e5761365e61355e565b500690565b60006020828403121561367557600080fd5b8151801515811461300d57600080fdfea2646970667358221220e908efe39e0e77807fc5212711016a09d4cac71728232ed8819b88109537a72564736f6c63430008190033