tl;dr The Config
types in your question are configuration parameters for the deposit of submitting a signed solution and reward for a signed solution accepted at the end of the signed phase.
Context
These are configurations for submitting solutions during the signed phase. For the canonical explanation of the phases see the module docs.
For quick a background though, the 3 phases supported by the election provider multi phase pallet are:
- Off Phase: Nothing is happening. At the threshold of the off and signed phase is when the election snapshot is taken. The off phase starts when the unsigned phase ends.
- Signed Phase: Anyone can submit a solution via the submit extrinsic. When a user submits a solution, some funds are reserved as a deposit. More on this below.
- Unsigned Phase: Block authors can submit an inherent with a solution mined by the offchain worker.
At the end of the signed phase the submitted solution with the best score is checked to ensure the claimed score matches the actual score. If it does we save it as the queued solution and reward the submitter, but if it does not we discard the solution, slash the submitters deposit, and repeat the process for the next best solution (see here for high level docs). When a solution is determined to have a correct score, the submitter is refunded the extrinsic fee, and the deposits based on SignedDepositBase
, SignedDepositByte
and a reward based on SignedRewardBase
- more on this below.
Here is an example of the flow for checking solutions at the end of the signed phase (see module docs here)
+-------------------------------+
|Solution(score=20, valid=false)| +--> Slashed
+-------------------------------+
|Solution(score=15, valid=true )| +--> Rewarded, Saved
+-------------------------------+
|Solution(score=10, valid=true )| +--> Discarded
+-------------------------------+
|Solution(score=05, valid=false)| +--> Discarded
+-------------------------------+
| None |
+-------------------------------+
Answer
The values are for configuring the deposit of submitting a solution and the reward for the highest scored solution at the end fo the signed phase.
Both values are calculated from within Call::submit
.
The deposit is calculated by deposit_for
, using SignedDepositByte
and SignedDepositBase
. SignedDepositByte
is multiplied by the length of the encoded solution to create 1 o 3 components for the deposit. SignedDepositBase
is a static component of the deposit that is added to the other deposit components to create the final deposit. (Note the estimated weight for the feasibility check of the solution is also multiplied by SignedDepositWeight
to create third deposit component). This deposit is then reserved here. Below is the exact logic:
/// Collect a sufficient deposit to store this solution.
///
/// The deposit is composed of 3 main elements:
///
/// 1. base deposit, fixed for all submissions.
/// 2. a per-byte deposit, for renting the state usage.
/// 3. a per-weight deposit, for the potential weight usage in an upcoming on_initialize
pub fn deposit_for(
raw_solution: &RawSolution<SolutionOf<T>>,
size: SolutionOrSnapshotSize,
) -> BalanceOf<T> {
let encoded_len: u32 = raw_solution.encoded_size().saturated_into();
let encoded_len: BalanceOf<T> = encoded_len.into();
let feasibility_weight = Self::feasibility_weight_of(raw_solution, size);
let len_deposit = T::SignedDepositByte::get().saturating_mul(encoded_len);
let weight_deposit =
T::SignedDepositWeight::get().saturating_mul(feasibility_weight.saturated_into());
T::SignedDepositBase::get()
.saturating_add(len_deposit)
.saturating_add(weight_deposit)
}
}
source
The reward is calculated by estimating the fee the user paid to submit the solution and adding that to the SignedRewardBase
. In practical terms, at the end of the signed phase, if the user submitted a wining solution, the cost of submission is reimbursed and they are given an award of SignedRewardBase
. The exact logic is below:
let reward = {
let call =
Call::submit { raw_solution: raw_solution.clone(), num_signed_submissions };
let call_fee = T::EstimateCallFee::estimate_call_fee(&call, None.into());
T::SignedRewardBase::get().saturating_add(call_fee)
};
source
Both the reward and deposit are stored with the submitted solution. Lets look at the logic for a when a winning solution is accepted to see how the reward and deposit are used:
/// Helper function for the case where a solution is accepted in the signed phase.
///
/// Extracted to facilitate with weight calculation.
///
/// Infallible
pub fn finalize_signed_phase_accept_solution(
ready_solution: ReadySolution<T::AccountId>,
who: &T::AccountId,
deposit: BalanceOf<T>,
reward: BalanceOf<T>,
) {
// write this ready solution.
<QueuedSolution<T>>::put(ready_solution);
// emit reward event
Self::deposit_event(crate::Event::Rewarded { account: who.clone(), value: reward });
// unreserve deposit.
let _remaining = T::Currency::unreserve(who, deposit);
debug_assert!(_remaining.is_zero());
// Reward.
let positive_imbalance = T::Currency::deposit_creating(who, reward);
T::RewardHandler::on_unbalanced(positive_imbalance);
}
source
The deposit is unreserved (it was originally reserved in Call:submit
) and the reward is deposited into the submitters account. Note that in polkadot the RewardHandler
is a default impl, thus the rewards are “minted”, increasing the total issuance.
Finally, lets take a look at what happens when a solution is rejected:
/// Helper function for the case where a solution is accepted in the rejected phase.
///
/// Extracted to facilitate with weight calculation.
///
/// Infallible
pub fn finalize_signed_phase_reject_solution(who: &T::AccountId, deposit: BalanceOf<T>) {
Self::deposit_event(crate::Event::Slashed { account: who.clone(), value: deposit });
let (negative_imbalance, _remaining) = T::Currency::slash_reserved(who, deposit);
debug_assert!(_remaining.is_zero());
T::SlashHandler::on_unbalanced(negative_imbalance);
}
source
The deposit is slashed from the users reserved funds. Note that in polkadot the SlashHandler
is a default impl, thus the slashes are “burned”, decreasing the total issuance.
Pulling it all together
The one missing piece is: how does one submit a signed solution? Parity has been working on a staking election solution miner. At the time of writing it lives here, in the polkadot repo. In theory this can be made generic for any chain that uses the staking and election provider multi pallets, but it was setup in the polkadot repo to have easy access to the concrete runtime types. It is still in development but in the future Parity hopes to see many miner implementations that compete on speed and, more importantly, the quality of the solution (which can be done by implementing different algorithms).