I would like clarification on whether the rate that is pulled from the reserve pool is set by delegates or hard coded (or maybe it is set by delegates but with a hard-coded upper limit?).
TLDR: Set by delegates, but with a hard-coded upper limit of
17 / 4294967296 of the reserve pool's value per second, with daily compounding.
I wrote the code which actually implements this, and was heavily involved in its design, so I can answer this question. (In fact the image xeroc posted in this thread is based on a design diagram I drew on a white board.)
The upper limit on witness plus worker pay is equal to a fraction of the reserve pool. Here's part of the
get_max_budget() function:
int64_t dt = (now - dpo.last_budget_time).to_seconds();
share_type reserve = core.burned(*this) + core_dd.accumulated_fees;
fc::uint128_t budget_u128 = reserve.value;
budget_u128 *= uint64_t(dt);
budget_u128 *= GRAPHENE_CORE_ASSET_CYCLE_RATE;
//round up to the nearest satoshi -- this is necessary to ensure
// there isn't an "untouchable" reserve, and we will eventually
// be able to use the entire reserve
budget_u128 += ((uint64_t(1) << GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS) - 1);
budget_u128 >>= GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS;
It sets an upper bound on the budget equal to the reserve pool times a hardcoded factor of
17 / 2**32 per day. Since we know the initial float (2.5 billion) and initial reserve (1.2 billion), this is enough information to calculate the annual monetary inflation with your favorite calculator (e.g. a Python shell):
>>> (1 - (1 - (17. / 2**32 * 60 * 60 * 24)) ** (365*1)) * 1.2e9 / 2.5e9
0.0563356758462306
It is a little tricky to get the compounding exactly right in the above computation (intraday is non-compounding, daily is compounding). That number's returned from
get_max_budget(), let's look at what happens to that number next:
share_type available_funds = get_max_budget( now );
share_type witness_budget = gpo.parameters.witness_pay_per_block.value * blocks_to_maint;
witness_budget = std::min( witness_budget, available_funds );
available_funds -= witness_budget;
fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value;
worker_budget_u128 *= uint64_t(time_to_maint);
worker_budget_u128 /= 60*60*24;
share_type worker_budget;
if( worker_budget_u128 >= available_funds.value )
worker_budget = available_funds;
else
worker_budget = worker_budget_u128.to_uint64();
available_funds -= worker_budget;
share_type leftover_worker_funds = worker_budget;
pay_workers( leftover_worker_funds );
available_funds += leftover_worker_funds;
// available_funds is money we could spend, but don't want to.
// we simply let it evaporate back into the reserve.
}
gpo.parameters is the list of parameters under the delegates' control. So the delegates set the witness pay per block and the total worker budget per day, to get a daily budget with two line items (witnesses and workers). This delegate-set budget is restricted based on
available_funds which is initially the result of
get_max_budget(), and reduced by the amount spent on each line item as it is processed. Witness are paid first, workers are paid second, and any leftover goes into the reserve.
Where I've used the word "daily" I actually mean "per maintenance block." The maintenance block frequency will initially be daily, but it is one of the delegate-settable parameters.