Skip to content

Commit 72957ec

Browse files
authored
Merge pull request #108 from SurfingNerd/key-gen-round-counter
Key gen round counter Merged. There are known issues with the backward compatibility that will get fixed soon.
2 parents 42362ed + e257f91 commit 72957ec

16 files changed

+281
-81
lines changed

contracts/KeyGenHistory.sol

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ contract KeyGenHistory is UpgradeabilityAdmin, IKeyGenHistory {
1313
// WARNING: since this contract is upgradeable, do not remove
1414
// existing storage variables and do not change their types!
1515

16-
// the current validator addresses
17-
address[] public validatorSet;
1816
mapping(address => bytes) public parts;
1917
mapping(address => bytes[]) public acks;
2018

@@ -27,6 +25,16 @@ contract KeyGenHistory is UpgradeabilityAdmin, IKeyGenHistory {
2725
/// @dev The address of the `ValidatorSetHbbft` contract.
2826
IValidatorSetHbbft public validatorSetContract;
2927

28+
/// @dev round counter for key generation rounds.
29+
/// in an ideal world, every key generation only requires one try,
30+
/// and all validators manage to write their acks and parts,
31+
/// so it is possible to achieve this goal in round 0.
32+
/// in the real world, there are failures,
33+
/// this mechanics helps covering that,
34+
/// by revoking transactions, that were targeted for an earlier key gen round.
35+
/// more infos: https://github.com/DMDcoin/hbbft-posdao-contracts/issues/106
36+
uint256 public currentKeyGenRound;
37+
3038
event NewValidatorsSet(address[] newValidatorSet);
3139

3240
/// @dev Ensures the `initialize` function was called before.
@@ -55,6 +63,14 @@ contract KeyGenHistory is UpgradeabilityAdmin, IKeyGenHistory {
5563
_;
5664
}
5765

66+
/// @dev ensures that Key Generation functions are called with wrong _epoch
67+
/// parameter to prevent old and wrong transactions get picked up.
68+
modifier onlyCorrectRound(uint _roundCounter) {
69+
require(currentKeyGenRound == _roundCounter,
70+
"Key Generation function called with wrong _roundCounter parameter.");
71+
_;
72+
}
73+
5874
/// @dev Clears the state (acks and parts of previous validators.
5975
/// @param _prevValidators The list of previous validators.
6076
function clearPrevKeyGenState(address[] calldata _prevValidators)
@@ -69,6 +85,19 @@ contract KeyGenHistory is UpgradeabilityAdmin, IKeyGenHistory {
6985
numberOfAcksWritten = 0;
7086
}
7187

88+
function notifyKeyGenFailed()
89+
external
90+
onlyValidatorSet {
91+
currentKeyGenRound = currentKeyGenRound + 1;
92+
}
93+
94+
function notifyNewEpoch()
95+
external
96+
onlyValidatorSet {
97+
currentKeyGenRound = 1;
98+
}
99+
100+
72101
function initialize(
73102
address _validatorSetContract,
74103
address[] memory _validators,
@@ -85,17 +114,19 @@ contract KeyGenHistory is UpgradeabilityAdmin, IKeyGenHistory {
85114
require(_validatorSetContract != address(0), "Validator contract address cannot be 0.");
86115

87116
validatorSetContract = IValidatorSetHbbft(_validatorSetContract);
88-
validatorSet = _validators;
89117

90118
for (uint256 i = 0; i < _validators.length; i++) {
91119
parts[_validators[i]] = _parts[i];
92120
acks[_validators[i]] = _acks[i];
93121
}
122+
123+
currentKeyGenRound = 1;
94124
}
95125

96-
function writePart(uint256 _upcommingEpoch, bytes memory _part)
126+
function writePart(uint256 _upcommingEpoch, uint256 _roundCounter, bytes memory _part)
97127
public
98-
onlyUpcommingEpoch(_upcommingEpoch) {
128+
onlyUpcommingEpoch(_upcommingEpoch)
129+
onlyCorrectRound(_roundCounter) {
99130
// It can only be called by a new validator which is elected but not yet finalized...
100131
// ...or by a validator which is already in the validator set.
101132
require(validatorSetContract.isPendingValidator(msg.sender), "Sender is not a pending validator");
@@ -104,9 +135,10 @@ contract KeyGenHistory is UpgradeabilityAdmin, IKeyGenHistory {
104135
numberOfPartsWritten++;
105136
}
106137

107-
function writeAcks(uint256 _upcommingEpoch, bytes[] memory _acks)
138+
function writeAcks(uint256 _upcommingEpoch, uint256 _roundCounter, bytes[] memory _acks)
108139
public
109-
onlyUpcommingEpoch(_upcommingEpoch) {
140+
onlyUpcommingEpoch(_upcommingEpoch)
141+
onlyCorrectRound(_roundCounter) {
110142
// It can only be called by a new validator which is elected but not yet finalized...
111143
// ...or by a validator which is already in the validator set.
112144
require(validatorSetContract.isPendingValidator(msg.sender), "Sender is not a pending validator");
@@ -129,6 +161,13 @@ contract KeyGenHistory is UpgradeabilityAdmin, IKeyGenHistory {
129161
return acks[val].length;
130162
}
131163

164+
function getCurrentKeyGenRound()
165+
external
166+
view
167+
returns(uint256) {
168+
return currentKeyGenRound;
169+
}
170+
132171
function getNumberOfKeyFragmentsWritten()
133172
external
134173
view

contracts/TxPermissionHbbft.sol

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,15 @@ contract TxPermissionHbbft is UpgradeableOwned, ITxPermission {
312312
} else {
313313
return (NONE, false);
314314
}
315+
316+
uint256 roundCounter = _getSliceUInt256(36, _data);
317+
318+
if (roundCounter == IStakingHbbft(validatorSetContract.stakingContract()).stakingEpoch() + 1) {
319+
return (CALL, false);
320+
} else {
321+
return (NONE, false);
322+
}
323+
315324
} else {
316325
// we want to write the Acks, but it's not time for write the Acks.
317326
// so this transaction is not allowed.
@@ -367,14 +376,13 @@ contract TxPermissionHbbft is UpgradeableOwned, ITxPermission {
367376
// bytes4(keccak256("reportMalicious(address,uint256,bytes)"))
368377
bytes4 public constant REPORT_MALICIOUS_SIGNATURE = 0xc476dd40;
369378

370-
// bytes4(keccak256("writePart(uint256,bytes)"))
371-
bytes4 public constant WRITE_PART_SIGNATURE = 0x0334657d;
379+
// bytes4(keccak256("writePart(uint256,uint256,bytes)"))
380+
bytes4 public constant WRITE_PART_SIGNATURE = 0x2d4de124;
372381

373-
// bytes4(keccak256("writeAcks(uint256,bytes[])"))
374-
bytes4 public constant WRITE_ACKS_SIGNATURE = 0xc56aef48;
382+
// bytes4(keccak256("writeAcks(uint256,uint256,bytes[])"))
383+
bytes4 public constant WRITE_ACKS_SIGNATURE = 0x5623208e;
375384

376-
// bytes4(keccak256("announceAvailability()"))
377-
bytes4 public constant ANNOUNCE_AVAILABILITY_SIGNATURE = 0x292525af;
385+
bytes4 public constant ANNOUNCE_AVAILABILITY_SIGNATURE = 0x43bcce9f;
378386

379387
/// @dev An internal function used by the `addAllowedSender` and `initialize` functions.
380388
/// @param _sender The address for which transactions of any type must be allowed.

contracts/ValidatorSetHbbft.sol

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,7 @@ contract ValidatorSetHbbft is UpgradeableOwned, IValidatorSetHbbft {
203203
_setStakingAddress(miningAddress, _initialStakingAddresses[i]);
204204
}
205205

206-
maxValidators = 7;
207-
206+
maxValidators = 25;
208207
}
209208

210209
/// @dev Called by the system when a pending validator set is ready to be activated.
@@ -224,6 +223,7 @@ contract ValidatorSetHbbft is UpgradeableOwned, IValidatorSetHbbft {
224223

225224
// new epoch starts
226225
stakingContract.incrementStakingEpoch();
226+
keyGenHistoryContract.notifyNewEpoch();
227227
delete _pendingValidators;
228228
stakingContract.setStakingEpochStartTime(this.getCurrentTimestamp());
229229

@@ -250,9 +250,17 @@ contract ValidatorSetHbbft is UpgradeableOwned, IValidatorSetHbbft {
250250

251251
/// @dev called by validators when a validator comes online after
252252
/// getting marked as unavailable caused by a failed key generation.
253-
function announceAvailability()
253+
function announceAvailability(uint256 _blockNumber, bytes32 _blockhash)
254254
external {
255-
require(canCallAnnounceAvailability(msg.sender), 'Announcing availability not possible');
255+
256+
require(canCallAnnounceAvailability(msg.sender), 'Announcing availability not possible.');
257+
require(_blockNumber < block.number, '_blockNumber argument must be in the past.');
258+
// 255 is a technical limitation of EVM ability to look into the past.
259+
// however, we query just for 16 blocks here.
260+
// this provides a time window big enough for valid nodes.
261+
require(_blockNumber + 16 > block.number, '_blockNumber argument must be in the past within the last 255 blocks.');
262+
// we have ensured now that we technicaly able to query the blockhash for that block
263+
require(blockhash(_blockNumber) == _blockhash, 'provided blockhash must match blockchains blockhash');
256264

257265
uint timestamp = this.getCurrentTimestamp();
258266
validatorAvailableSince[msg.sender] = timestamp;
@@ -321,6 +329,8 @@ contract ValidatorSetHbbft is UpgradeableOwned, IValidatorSetHbbft {
321329
= keyGenHistoryContract.getNumberOfKeyFragmentsWritten();
322330

323331

332+
//address[] memory badValidators = new address[];
333+
324334
for(uint i = 0; i < _pendingValidators.length; i++) {
325335

326336
// get mining address for this pool.
@@ -364,6 +374,9 @@ contract ValidatorSetHbbft is UpgradeableOwned, IValidatorSetHbbft {
364374
}
365375
}
366376

377+
keyGenHistoryContract.clearPrevKeyGenState(_pendingValidators);
378+
keyGenHistoryContract.notifyKeyGenFailed();
379+
367380
// we might only set a subset to the newValidatorSet function,
368381
// since the last indexes of the array are holding unused slots.
369382
address[] memory forcedPools = new address[](goodValidatorsCount);
@@ -842,6 +855,29 @@ contract ValidatorSetHbbft is UpgradeableOwned, IValidatorSetHbbft {
842855
// Remove pools marked as `to be removed`
843856
stakingContract.removePools();
844857
}
858+
859+
// a new validator set can get choosen already outside the timeframe for phase 2.
860+
// this can happen if the network got stuck and get's repaired.
861+
// and the repair takes longer than a single epoch.
862+
// we detect this case here and grant an extra time window
863+
// so the selected nodes also get their chance to write their keys.
864+
// more about: https://github.com/DMDcoin/hbbft-posdao-contracts/issues/96
865+
866+
// timescale:
867+
// epoch start time ..... phase 2 transition .... current end of phase 2 ..... now ..... new end of phase 2.
868+
869+
// new extra window size has to cover the difference between phase2 transition and now.
870+
// to reach the new end of phase 2.
871+
872+
// current end of phase 2 : stakingContract.stakingFixedEpochEndTime()
873+
874+
// now: validatorSetContract.getCurrentTimestamp();
875+
876+
if (this.getCurrentTimestamp() > stakingContract.stakingFixedEpochEndTime()) {
877+
stakingContract.notifyNetworkOfftimeDetected(this.getCurrentTimestamp()
878+
- stakingContract.stakingFixedEpochEndTime());
879+
}
880+
845881
}
846882

847883
/// @dev Sets a new validator set stored in `_pendingValidators` array.

contracts/base/BlockRewardHbbftBase.sol

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ contract BlockRewardHbbftBase is UpgradeableOwned, IBlockRewardHbbft {
193193
/// starting a new staking epoch, snapshotting staking amounts for the upcoming staking epoch,
194194
/// and rewards distributing at the end of a staking epoch.
195195
/// @param _isEpochEndBlock Indicates if this is the last block of the current epoch i.e.
196-
/// just before the pending validators are fiinalized.
196+
/// just before the pending validators are finalized.
197197
function reward(bool _isEpochEndBlock)
198198
external
199199
onlySystem
@@ -247,7 +247,9 @@ contract BlockRewardHbbftBase is UpgradeableOwned, IBlockRewardHbbft {
247247

248248
uint256 phaseTransitionTime = stakingContract.startTimeOfNextPhaseTransition();
249249
uint256 currentTimestamp = validatorSetContract.getCurrentTimestamp();
250-
250+
251+
// TODO: Problem occurs here if there are not regular blocks:
252+
// https://github.com/DMDcoin/hbbft-posdao-contracts/issues/96
251253

252254
//we are in a transition to phase 2 if the time for it arrived,
253255
// and we do not have pendingValidators yet.
@@ -258,9 +260,25 @@ contract BlockRewardHbbftBase is UpgradeableOwned, IBlockRewardHbbft {
258260
if (isPhaseTransition) {
259261
// Choose new validators
260262
validatorSetContract.newValidatorSet();
261-
} else if (currentTimestamp >= stakingContract.stakingFixedEpochEndTime() ) {
263+
} else if (currentTimestamp >= stakingContract.stakingFixedEpochEndTime()) {
262264
validatorSetContract.handleFailedKeyGeneration();
263265
}
266+
// } else {
267+
268+
// // check for faster validator set upscaling
269+
// // https://github.com/DMDcoin/hbbft-posdao-contracts/issues/90
270+
271+
// address[] memory miningAddresses = validatorSetContract.getValidators();
272+
273+
// // if there is a miningset that is smaller than the 2/3 of the maxValidators,
274+
// // then we choose the next epoch set.
275+
// if (miningAddresses.length < (validatorSetContract.maxValidators() / 3) * 2) {
276+
// address[] memory poolsToBeElected = stakingContract.getPoolsToBeElected();
277+
// if (poolsToBeElected.length > miningAddresses.length) {
278+
// validatorSetContract.newValidatorSet();
279+
// }
280+
// }
281+
// }
264282
}
265283
}
266284

contracts/base/StakingHbbftBase.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,21 @@ contract StakingHbbftBase is UpgradeableOwned, IStakingHbbft {
611611
currentKeyGenExtraTimeWindow += stakingTransitionTimeframeLength;
612612
}
613613

614+
/// @dev Notifies hbbft staking contract about a detected
615+
/// network offline time.
616+
/// if there is no handling for this,
617+
/// validators got chosen outside the transition timewindow
618+
/// and get banned immediatly, since they never got their chance
619+
/// to write their keys.
620+
/// more about: https://github.com/DMDcoin/hbbft-posdao-contracts/issues/96
621+
function notifyNetworkOfftimeDetected(uint256 detectedOfflineTime)
622+
public
623+
onlyValidatorSetContract {
624+
currentKeyGenExtraTimeWindow = currentKeyGenExtraTimeWindow
625+
+ detectedOfflineTime
626+
+ stakingTransitionTimeframeLength;
627+
}
628+
614629
/// @dev Notifies hbbft staking contract that a validator
615630
/// asociated with the given `_stakingAddress` became
616631
/// available again and can be put on to the list
@@ -621,6 +636,7 @@ contract StakingHbbftBase is UpgradeableOwned, IStakingHbbft {
621636
{
622637
if (stakeAmount[_stakingAddress][_stakingAddress] >= candidateMinStake) {
623638
_addPoolActive(_stakingAddress, true);
639+
_setLikelihood(_stakingAddress);
624640
}
625641
}
626642

contracts/interfaces/IKeyGenHistory.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,9 @@ interface IKeyGenHistory {
1111
function clearPrevKeyGenState(address[] calldata) external;
1212
function getAcksLength(address val) external view returns(uint256);
1313
function getPart(address val) external view returns (bytes memory);
14+
function getCurrentKeyGenRound() external view returns(uint256);
1415
function getNumberOfKeyFragmentsWritten() external view returns(uint128, uint128);
16+
function notifyNewEpoch() external;
17+
function notifyKeyGenFailed() external;
18+
1519
}

contracts/interfaces/IStakingHbbft.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ interface IStakingHbbft {
1818
function setStakingEpochStartTime(uint256) external;
1919
function notifyKeyGenFailed() external;
2020
function notifyAvailability(address _stakingAddress) external;
21+
function notifyNetworkOfftimeDetected(uint256) external;
2122
function getPoolPublicKey(address _poolAddress) external view returns (bytes memory);
2223
function getPoolsLikelihood() external view returns(uint256[] memory, uint256);
2324
function getPoolsToBeElected() external view returns(address[] memory);

contracts/interfaces/IValidatorSetHbbft.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface IValidatorSetHbbft {
1414
address[] calldata,
1515
address[] calldata
1616
) external;
17-
function announceAvailability() external;
17+
function announceAvailability(uint256, bytes32) external;
1818
function finalizeChange() external;
1919
function newValidatorSet() external;
2020
function removeMaliciousValidators(address[] calldata) external;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"type": "git",
2828
"url": "git+https://github.com/poanetwork/posdao-contracts.git"
2929
},
30-
"author": "[email protected]",
30+
"author": "[email protected]",
3131
"bugs": {
3232
"url": "https://github.com/poanetwork/posdao-contracts/issues"
3333
},

scripts/check-node-state.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/bash
2+
3+
#queryOptions='-H "Content-Type: application/json" -X POST localhost:8540'
4+
#echo $queryOptions
5+
6+
# 0x60e5c520000000000000000000000000f8a7d150d9290a665792f9af15d17df477bacb9e
7+
address=
8+
9+
availableSince=$(curl -s --data '{"method":"eth_call","params":[{"from":"0x407d73d8a49eeb85d32cf465507dd71d507100c1","to":"0x1000000000000000000000000000000000000001","data":"0x60e5c520000000000000000000000000f8a7d150d9290a665792f9af15d17df477bacb9e"}],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8540)
10+
# availableSince=$(curl -s --data '{"method":"eth_call","params":[{"from":"0x407d73d8a49eeb85d32cf465507dd71d507100c1","to":"0x1000000000000000000000000000000000000001","data":"0x60e5c520000000000000000000000000f8a7d150d9290a665792f9af15d17df477bacb9e"}],"id":1,"jsonrpc":"2.0"}' $queryOptions)
11+
12+
# if availaible since contains zero, then we know that we are not available.
13+
14+
15+
if [[ $availableSince == *"0x0000000000000000000000000000000000000000000000000000000000000000"* ]]; then
16+
echo "we need to restart!"
17+
PID=pidof openethereum
18+
echo "kill $PID"
19+
sleep 15
20+
echo "starting node again."
21+
sh start-node.sh &
22+
fi
23+
24+
25+
#curl -s --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8540 | grep -Po '"result":.*?[^\\]",'
26+
#curl -s --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8540 | grep -Po 's/"result"://; s/^"//; s/",$//'
27+
28+
29+
30+
# blockNumber=$(curl -s --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8540 | jq -r '.result')
31+
32+
# echo "we are on block number $blockNumber"
33+
34+
# blockTime=$(curl -s --data '{"method":"eth_getBlockByNumber","params":["'$blockNumber'", true],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8540 | jq -r '.result.timestamp')
35+
36+
# echo "The decimal value of $blockTime=%d\n" $((16#$blockTime))
37+
38+
# echo $blockTime
39+

0 commit comments

Comments
 (0)