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.0pragma 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.0pragma 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.0pragma 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.0pragma 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.0pragma 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.0pragma 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.0pragma 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