In order to introduce more advanced and optimized strategies of keeper selection, PowerAgent will be completed with different strategies of selection, the choice of which will be optional depending on the situation.
The most prioritized strategy by now is random keeper selection.
One can simply take
block.difficultyas a source for the random number to later use it to select a keeper. However, this number does not satisfy the condition of not being deterministic.
Other, more decentralized way is to query a pseudorandom number by RanDAO. RanDAO is a blockchain based verifiable random number generator, which provides a non-deterministic pseudo random number for any contracts’ disposal.
In the updated contract, an
EnumerableSet.UintSet internal activeKeeperswill hold numerated keepers that are active and ready to be assigned to a Job.
function assignNextKeeper(bytes32 jobKey) internalis responsible for choosing a random keeper from the set of active keepers and assigning it to the job with
The main step is to randomly choose the keeper (by its index) from the set:
index = (_getPseudoRandom() + jobKey) % NumberOfKeepers
_getPseudoRandom()yields the pseudo random number by one of the underlying algorithms described above.
After that the keeper by the index is selected and undergoes trial for the job prerequisites.
For the keeper to be successfully assigned, the following conditions must be met:
- Keeper has sufficient stake for this Job.
- Keeper must be active.
indexis increased by 1 and the next keeper from the set is checked until one satisfying the conditions is met.
On successful keeper selection, the function will
emit KeeperJobLock(nextExecutionKeeperId, jobKey).
The infromation on the Job's keeper that will perform the next execution and the keeper's jobs is stored in the mappings respectively:
mapping(bytes32 => uint256) public jobNextKeeperId
mapping(uint256 => EnumerableSet.Bytes32Set) internal keeperLocksByJob
The assignment function is called every time a new Job is registered, a Job config is changed, a Job is topped up with credits, and, of course, after each Job execution.
The keeper is released from the job automatically after the job has been executed by this keeper, and the next keeper is assigned.
Also, a helper function
releaseKeeperIfRequired(jobKey, expectedKeeperId)is called each time a Job owner changes the config or withdraws credits from the Job to prevent keepers from executing a task they will not receive payment for.
On special occasions the keeper can be released from the assigned job by the keeper themselves. This function may be needed when, for example, a keeper may decide to discontinue participation and withdraw their CVP stake.
function releaseJob(uint256 keeperId_, bytes32 jobKey_) externalis responsible for this. It can be called by the keeper admin contract.
The following requirements must be met:
- The keeper is assigned to the job specified in the arguments.
- For interval jobs, the next execution should not be awaited.
- The keeper must not undergo slashing at the time of the release.
- The job must not be resolver type.
If all met, the keeper is released from its duty.