Skip to content

Commit

Permalink
Queue of InitiateChange events
Browse files Browse the repository at this point in the history
Solves the issue: if `InitiateChange` has already been emitted but not yet finalized, other `InitiateChange` events emitted during these several blocks (between emitting and finalizing) will be ignored by Parity.
  • Loading branch information
varasev committed Jan 25, 2019
1 parent e53da57 commit d8e9c18
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 35 deletions.
2 changes: 1 addition & 1 deletion contracts/ValidatorSetAuRa.sol
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ contract ValidatorSetAuRa is IValidatorSetAuRa, ValidatorSetBase {
if (_removeMaliciousValidator(_validator)) {
// From this moment `getPendingValidators()` will return the new validator set
_incrementChangeRequestCount();
_setInitiateChangeAllowed(true);
_enqueuePendingValidators(false);
}
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/ValidatorSetHBBFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ contract ValidatorSetHBBFT is ValidatorSetBase {
if (validatorSetChanged) {
// From this moment `getPendingValidators()` will return the new validator set
_incrementChangeRequestCount();
_setInitiateChangeAllowed(true);
_enqueuePendingValidators(false);
}
}

Expand Down
131 changes: 98 additions & 33 deletions contracts/abstracts/ValidatorSetBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,12 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
function emitInitiateChange() external {
if (!isValidator(msg.sender)) return;
if (!initiateChangeAllowed()) return;
emit InitiateChange(blockhash(block.number - 1), getPendingValidators());
_setInitiateChangeAllowed(false);
(address[] memory newSet, bool newStakingEpoch) = _dequeuePendingValidators();
if (newSet.length > 0) {
emit InitiateChange(blockhash(block.number - 1), newSet);
_setInitiateChangeAllowed(false);
_setQueueValidators(newSet, newStakingEpoch);
}
}

function removePool() public gasPriceIsValid {
Expand All @@ -104,32 +108,37 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
}

function finalizeChange() public onlySystem {
if (validatorSetApplyBlock() == 0) {
// Apply new validator set after `newValidatorSet()` is called
if (block.number > 1) { // skip the block #1 (the block after genesis)
(address[] memory queueValidators, bool newStakingEpoch) = getQueueValidators();

address[] memory previousValidators = getPreviousValidators();
address[] memory currentValidators = getValidators();
uint256 i;
if (validatorSetApplyBlock() == 0 && newStakingEpoch) {
// Apply new validator set after `newValidatorSet()` is called

// Save the previous validator set
for (i = 0; i < previousValidators.length; i++) {
_setIsValidatorOnPreviousEpoch(previousValidators[i], false);
}
for (i = 0; i < currentValidators.length; i++) {
_setIsValidatorOnPreviousEpoch(currentValidators[i], true);
}
_setPreviousValidators(currentValidators);
address[] memory previousValidators = getPreviousValidators();
address[] memory currentValidators = getValidators();
uint256 i;

_applyPendingValidators();
// Save the previous validator set
for (i = 0; i < previousValidators.length; i++) {
_setIsValidatorOnPreviousEpoch(previousValidators[i], false);
}
for (i = 0; i < currentValidators.length; i++) {
_setIsValidatorOnPreviousEpoch(currentValidators[i], true);
}
_setPreviousValidators(currentValidators);

_setValidatorSetApplyBlock(_getCurrentBlockNumber());
_applyQueueValidators(queueValidators);

// Set a new snapshot inside BlockReward contract
IBlockReward(blockRewardContract()).setSnapshot();
} else {
// Apply new validator set after `reportMalicious` is called
_applyPendingValidators();
_setValidatorSetApplyBlock(_getCurrentBlockNumber());

// Set a new snapshot inside BlockReward contract
IBlockReward(blockRewardContract()).setSnapshot();
} else {
// Apply new validator set after `reportMalicious` is called
_applyQueueValidators(queueValidators);
}
}
_setInitiateChangeAllowed(true);
}

function moveStake(address _fromPool, address _toPool, uint256 _amount) public gasPriceIsValid {
Expand Down Expand Up @@ -224,6 +233,10 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
return addressArrayStorage[PENDING_VALIDATORS];
}

function getQueueValidators() public view returns(address[] memory, bool) {
return (addressArrayStorage[QUEUE_VALIDATORS], boolStorage[QUEUE_VALIDATORS_NEW_STAKING_EPOCH]);
}

function getCandidateMinStake() public view returns(uint256) {
return uintStorage[CANDIDATE_MIN_STAKE];
}
Expand Down Expand Up @@ -397,6 +410,10 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
bytes32 internal constant POOLS_EMPTY = keccak256("poolsEmpty");
bytes32 internal constant POOLS_NON_EMPTY = keccak256("poolsNonEmpty");
bytes32 internal constant PREVIOUS_VALIDATORS = keccak256("previousValidators");
bytes32 internal constant QUEUE_PV_FIRST = keccak256("queuePVFirst");
bytes32 internal constant QUEUE_PV_LAST = keccak256("queuePVLast");
bytes32 internal constant QUEUE_VALIDATORS = keccak256("queueValidators");
bytes32 internal constant QUEUE_VALIDATORS_NEW_STAKING_EPOCH = keccak256("queueValidatorsNewStakingEpoch");
bytes32 internal constant RANDOM_CONTRACT = keccak256("randomContract");
bytes32 internal constant STAKING_EPOCH = keccak256("stakingEpoch");
bytes32 internal constant VALIDATOR_SET_APPLY_BLOCK = keccak256("validatorSetApplyBlock");
Expand All @@ -409,6 +426,9 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
bytes32 internal constant POOL_DELEGATOR_INDEX = "poolDelegatorIndex";
bytes32 internal constant POOL_INDEX = "poolIndex";
bytes32 internal constant POOL_INACTIVE_INDEX = "poolInactiveIndex";
bytes32 internal constant QUEUE_PV_BLOCK = "queuePVBlock";
bytes32 internal constant QUEUE_PV_LIST = "queuePVList";
bytes32 internal constant QUEUE_PV_NEW_EPOCH = "queuePVNewEpoch";
bytes32 internal constant STAKE_AMOUNT = "stakeAmount";
bytes32 internal constant STAKE_AMOUNT_BY_EPOCH = "stakeAmountByEpoch";
bytes32 internal constant STAKE_AMOUNT_TOTAL = "stakeAmountTotal";
Expand Down Expand Up @@ -463,23 +483,22 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
}
}

function _applyPendingValidators() internal {
address[] memory validators = getValidators();
function _applyQueueValidators(address[] memory _queueValidators) internal {
address[] memory prevValidators = getValidators();
uint256 i;

// Clear indexes for old validator set
for (i = 0; i < validators.length; i++) {
_setValidatorIndex(validators[i], 0);
_setIsValidator(validators[i], false);
for (i = 0; i < prevValidators.length; i++) {
_setValidatorIndex(prevValidators[i], 0);
_setIsValidator(prevValidators[i], false);
}

validators = getPendingValidators();
_setCurrentValidators(validators);
_setCurrentValidators(_queueValidators);

// Set indexes for new validator set
for (i = 0; i < validators.length; i++) {
_setValidatorIndex(validators[i], i);
_setIsValidator(validators[i], true);
for (i = 0; i < _queueValidators.length; i++) {
_setValidatorIndex(_queueValidators[i], i);
_setIsValidator(_queueValidators[i], true);
}
}

Expand All @@ -489,6 +508,44 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
] = _bannedUntil;
}

function _enqueuePendingValidators(bool _newStakingEpoch) internal {
uint256 queueFirst = uintStorage[QUEUE_PV_FIRST];
uint256 queueLast = uintStorage[QUEUE_PV_LAST];

for (uint256 i = queueLast; i >= queueFirst; i--) {
if (
uintStorage[keccak256(abi.encode(QUEUE_PV_BLOCK, i))] == block.number &&
!boolStorage[keccak256(abi.encode(QUEUE_PV_NEW_EPOCH, i))]
) {
addressArrayStorage[keccak256(abi.encode(QUEUE_PV_LIST, i))] = getPendingValidators();
return;
}
}

queueLast++;
addressArrayStorage[keccak256(abi.encode(QUEUE_PV_LIST, queueLast))] = getPendingValidators();
boolStorage[keccak256(abi.encode(QUEUE_PV_NEW_EPOCH, queueLast))] = _newStakingEpoch;
uintStorage[keccak256(abi.encode(QUEUE_PV_BLOCK, queueLast))] = block.number;
uintStorage[QUEUE_PV_LAST] = queueLast;
}

function _dequeuePendingValidators() internal returns(address[] memory newSet, bool newStakingEpoch) {
uint256 queueFirst = uintStorage[QUEUE_PV_FIRST];
uint256 queueLast = uintStorage[QUEUE_PV_LAST];

if (queueLast < queueFirst) {
newSet = new address[](0);
newStakingEpoch = false;
} else {
newSet = addressArrayStorage[keccak256(abi.encode(QUEUE_PV_LIST, queueFirst))];
newStakingEpoch = boolStorage[keccak256(abi.encode(QUEUE_PV_NEW_EPOCH, queueFirst))];
delete addressArrayStorage[keccak256(abi.encode(QUEUE_PV_LIST, queueFirst))];
delete boolStorage[keccak256(abi.encode(QUEUE_PV_NEW_EPOCH, queueFirst))];
delete uintStorage[keccak256(abi.encode(QUEUE_PV_BLOCK, queueFirst))];
uintStorage[QUEUE_PV_FIRST]++;
}
}

function _incrementChangeRequestCount() internal {
uintStorage[CHANGE_REQUEST_COUNT]++;
}
Expand Down Expand Up @@ -533,6 +590,9 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
_setCandidateMinStake(_candidateMinStake);

_setValidatorSetApplyBlock(1);

uintStorage[QUEUE_PV_FIRST] = 1;
uintStorage[QUEUE_PV_LAST] = 0;
}

function _newValidatorSet() internal {
Expand Down Expand Up @@ -596,7 +656,7 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
_incrementStakingEpoch();
_incrementChangeRequestCount();

_setInitiateChangeAllowed(true);
_enqueuePendingValidators(true);
_setValidatorSetApplyBlock(0);
}

Expand Down Expand Up @@ -671,6 +731,11 @@ contract ValidatorSetBase is OwnedEternalStorage, IValidatorSet {
uintStorage[keccak256(abi.encode(POOL_DELEGATOR_INDEX, _pool, _delegator))] = _index;
}

function _setQueueValidators(address[] memory _validators, bool _newStakingEpoch) internal {
addressArrayStorage[QUEUE_VALIDATORS] = _validators;
boolStorage[QUEUE_VALIDATORS_NEW_STAKING_EPOCH] = _newStakingEpoch;
}

// Add `_delegator` to the array of pool's delegators
function _addPoolDelegator(address _pool, address _delegator) internal {
address[] storage delegators = addressArrayStorage[
Expand Down

0 comments on commit d8e9c18

Please sign in to comment.