Skip to content

Commit

Permalink
Merge pull request #97 from SurfingNerd/testnet-alpha-2
Browse files Browse the repository at this point in the history
Automatic validator count sweet spot targeting and permission fixes
  • Loading branch information
SurfingNerd authored Jul 25, 2021
2 parents 3ec3227 + d76da5a commit 42362ed
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 266 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
push:
pull_request:

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [10.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ node_modules
# hbbft-posdao-contracts specific files
.network
.pk
.mnemonic
.mnemonic*
4 changes: 2 additions & 2 deletions contracts/CertifierHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ contract CertifierHbbft is UpgradeableOwned, ICertifier {
external
view
returns(bool) {

if (_certified[_who]) {
return true;
}
Expand All @@ -101,9 +102,8 @@ contract CertifierHbbft is UpgradeableOwned, ICertifier {
// since the node cache the list of certifiers
// and the permission contracts checks anyway,
// if the specific 0 gas transaction is allowed or not.

IStakingHbbft stakingContract = IStakingHbbft(validatorSetContract.stakingContract());
return stakingContract.isPoolActive(stakingAddress);
return stakingAddress != address(0);
}

/// @dev Returns a boolean flag indicating whether the specified address is allowed to use zero gas price
Expand Down
1 change: 1 addition & 0 deletions contracts/TxPermissionHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ contract TxPermissionHbbft is UpgradeableOwned, ITxPermission {
validatorSetContract = IValidatorSetHbbft(_validatorSet);
keyGenHistoryContract = IKeyGenHistory(_keyGenHistoryContract);
minimumGasPrice = 1000000000; // (1 gwei)
blockGasLimit = 1000000000; // 1 giga gas block
}

/// @dev Adds the address for which transactions of any type must be allowed.
Expand Down
67 changes: 57 additions & 10 deletions contracts/ValidatorSetHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import "./interfaces/IKeyGenHistory.sol";
import "./interfaces/IRandomHbbft.sol";
import "./interfaces/IStakingHbbft.sol";
import "./interfaces/IValidatorSetHbbft.sol";
import "./upgradeability/UpgradeabilityAdmin.sol";
import "./upgradeability/UpgradeableOwned.sol";
import "./libs/SafeMath.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 UpgradeabilityAdmin, IValidatorSetHbbft {
contract ValidatorSetHbbft is UpgradeableOwned, IValidatorSetHbbft {
using SafeMath for uint256;

// =============================================== Storage ========================================================
Expand Down Expand Up @@ -91,10 +91,9 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
/// the value is of type timestamp
mapping(address => uint256) public validatorAvailableSince;

// ============================================== Constants =======================================================

/// @dev The max number of validators.
uint256 public constant MAX_VALIDATORS = 25;
uint256 public maxValidators;

// ================================================ Events ========================================================

Expand Down Expand Up @@ -204,6 +203,7 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
_setStakingAddress(miningAddress, _initialStakingAddresses[i]);
}

maxValidators = 7;

}

Expand All @@ -230,7 +230,7 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
}

/// @dev Implements the logic which forms a new validator set. If the number of active pools
/// is greater than MAX_VALIDATORS, the logic chooses the validators randomly using a random seed generated and
/// 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()
Expand Down Expand Up @@ -285,6 +285,13 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
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.

Expand Down Expand Up @@ -364,8 +371,21 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
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();
_newValidatorSet(forcedPools);

// 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 Reports that the malicious validator misbehaved at the specified block.
Expand Down Expand Up @@ -444,6 +464,12 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
_setStakingAddress(_miningAddress, _stakingAddress);
}

function setMaxValidators(uint256 _maxValidators)
external
onlyOwner {
maxValidators = _maxValidators;
}

// =============================================== Getters ========================================================

/// @dev Returns a boolean flag indicating whether delegators of the specified pool are currently banned.
Expand Down Expand Up @@ -715,6 +741,22 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
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
view
returns(uint256) {
require(_possibleValidatorCount > 0, "_possibleValidatorCount must not be 0");
if (_possibleValidatorCount < 4) {
return _possibleValidatorCount;
}
return ((_possibleValidatorCount - 1) / 3) * 3 + 1;
}

// ============================================== Internal ========================================================

/// @dev Updates the total reporting counter (see the `reportingCounterTotal` public mapping) for the current
Expand All @@ -736,17 +778,22 @@ contract ValidatorSetHbbft is UpgradeabilityAdmin, IValidatorSetHbbft {
reportingCounterTotal[currentStakingEpoch] = 0;
}
}

function _newValidatorSet(address[] memory _forcedPools)
internal
{
address[] memory poolsToBeElected = stakingContract.getPoolsToBeElected();
// Choose new validators
if (poolsToBeElected.length > MAX_VALIDATORS) {

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[](MAX_VALIDATORS);
address[] memory newValidators = new address[](numOfValidatorsToBeElected);

uint256 indexNewValidator = 0;
for(uint256 iForced = 0; iForced < _forcedPools.length; iForced++) {
Expand Down
3 changes: 1 addition & 2 deletions contracts/base/BlockRewardHbbftBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,7 @@ contract BlockRewardHbbftBase is UpgradeableOwned, IBlockRewardHbbft {
// Choose new validators
validatorSetContract.newValidatorSet();
} else if (currentTimestamp >= stakingContract.stakingFixedEpochEndTime() ) {
// removed, because availability handling is not implemented yet in the client implementation.
// validatorSetContract.handleFailedKeyGeneration();
validatorSetContract.handleFailedKeyGeneration();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IValidatorSetHbbft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface IValidatorSetHbbft {
function isValidatorOrPending(address) external view returns(bool);
function isPendingValidator(address) external view returns(bool);
function getPendingValidatorKeyGenerationMode(address) external view returns(KeyGenMode);
function MAX_VALIDATORS() external view returns(uint256); // solhint-disable-line func-name-mixedcase
function maxValidators() external view returns(uint256);
function miningByStakingAddress(address) external view returns(address);
function randomContract() external view returns(address);
function reportMaliciousCallable(address, address, uint256) external view returns(bool, bool);
Expand Down
Loading

0 comments on commit 42362ed

Please sign in to comment.