Skip to content

Commit d86429c

Browse files
authored
Merge pull request #1227 from input-output-hk/reward-settings
expose the reward parameters
2 parents a415280 + d871f8e commit d86429c

File tree

4 files changed

+237
-13
lines changed

4 files changed

+237
-13
lines changed

jormungandr-lib/src/interfaces/block0_configuration/DOCUMENTED_EXAMPLE.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,42 @@ blockchain_configuration:
103103
# to 10000. Uncomment the following line to apply a max limit:
104104
# max_limit: 10000
105105

106+
# Set the total reward supply available for monetary creation
107+
#
108+
# if not set there is no monetary creation
109+
# once emptied, there is no more monetary creation
110+
total_reward_supply: 100000000000000
111+
112+
# set the reward supply consumption. These parameters will define how the
113+
# total_reward_supply is consumed for the stake pool reward
114+
#
115+
# There's fundamentally many potential choices for how rewards are contributed back, and here's two potential valid examples:
116+
#
117+
# Linear formula: constant - ratio * (#epoch after epoch_start / epoch_rate)
118+
# Halving formula: constant * ratio ^ (#epoch after epoch_start / epoch_rate)
119+
#
120+
reward_parameters:
121+
halving: # or use "linear" for the linear formula
122+
# In the linear formula, it represents the starting point of the contribution
123+
# at #epoch=0, whereas in halving formula is used as starting constant for
124+
# the calculation.
125+
constant: 100
126+
127+
# In the halving formula, an effective value between 0.0 to 1.0 indicates a
128+
# reducing contribution, whereas above 1.0 it indicate an acceleration of contribution.
129+
#
130+
# However in linear formula the meaning is just a scaling factor for the epoch zone
131+
# (current_epoch - start_epoch / epoch_rate). Further requirement is that this ratio
132+
# is expressed in fractional form (e.g. 1/2), which allow calculation in integer form.
133+
ratio: "13/19"
134+
135+
# indicates when this contribution start. note that if the epoch is not
136+
# the same or after the epoch_start, the overall contribution is zero.
137+
epoch_start: 1
138+
139+
# the rate at which the contribution is tweaked related to epoch.
140+
epoch_rate: 3
141+
106142
# Initial state of the ledger. Each item is applied in order of this list
107143
initial:
108144
# Initial deposits present in the blockchain

jormungandr-lib/src/interfaces/block0_configuration/initial_config.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
interfaces::{
33
ActiveSlotCoefficient, BFTSlotsRatio, ConsensusLeaderId, KESUpdateSpeed, LinearFeeDef,
4-
NumberOfSlotsPerEpoch, SlotDuration, TaxType, Value,
4+
NumberOfSlotsPerEpoch, RewardParams, SlotDuration, TaxType, Value,
55
},
66
time::SecondsSinceUnixEpoch,
77
};
@@ -116,7 +116,12 @@ pub struct BlockchainConfiguration {
116116

117117
/// Set the value of the reward pot. if omitted then the reward pot is empty
118118
#[serde(default)]
119-
pub rewards: Option<Value>,
119+
pub total_reward_supply: Option<Value>,
120+
121+
/// The reward settings for the reward policy. No reward settings means no reward
122+
/// distributed at all.
123+
#[serde(default)]
124+
pub reward_parameters: Option<RewardParams>,
120125
}
121126

122127
impl From<BlockchainConfiguration> for ConfigParams {
@@ -166,7 +171,8 @@ impl BlockchainConfiguration {
166171
epoch_stability_depth: None,
167172
treasury: None,
168173
treasury_parameters: None,
169-
rewards: None,
174+
total_reward_supply: None,
175+
reward_parameters: None,
170176
}
171177
}
172178

@@ -189,7 +195,8 @@ impl BlockchainConfiguration {
189195
let mut kes_update_speed = None;
190196
let mut treasury = None;
191197
let mut treasury_parameters = None;
192-
let mut rewards = None;
198+
let mut total_reward_supply = None;
199+
let mut reward_parameters = None;
193200
let mut per_certificate_fees = None;
194201

195202
for param in params.iter().cloned() {
@@ -244,10 +251,12 @@ impl BlockchainConfiguration {
244251
ConfigParam::TreasuryParams(param) => treasury_parameters
245252
.replace(param.into())
246253
.map(|_| "treasury_parameters"),
247-
ConfigParam::RewardPot(param) => {
248-
rewards.replace(param.into()).map(|_| "reward-pot")
249-
}
250-
ConfigParam::RewardParams(_) => unimplemented!(),
254+
ConfigParam::RewardPot(param) => total_reward_supply
255+
.replace(param.into())
256+
.map(|_| "total_reward_supply"),
257+
ConfigParam::RewardParams(param) => reward_parameters
258+
.replace(param.into())
259+
.map(|_| "reward_parameters"),
251260
ConfigParam::PerCertificateFees(param) => per_certificate_fees
252261
.replace(param)
253262
.map(|_| "per_certificate_fees"),
@@ -280,7 +289,8 @@ impl BlockchainConfiguration {
280289
max_number_of_transactions_per_block,
281290
treasury,
282291
treasury_parameters,
283-
rewards,
292+
total_reward_supply,
293+
reward_parameters,
284294
})
285295
}
286296

@@ -300,7 +310,8 @@ impl BlockchainConfiguration {
300310
epoch_stability_depth,
301311
treasury,
302312
treasury_parameters,
303-
rewards,
313+
total_reward_supply,
314+
reward_parameters,
304315
} = self;
305316

306317
let mut params = ConfigParams::new();
@@ -336,8 +347,12 @@ impl BlockchainConfiguration {
336347
params.push(ConfigParam::TreasuryParams(treasury_parameters.into()));
337348
}
338349

339-
if let Some(rewards) = rewards {
340-
params.push(ConfigParam::RewardPot(rewards.into()));
350+
if let Some(total_reward_supply) = total_reward_supply {
351+
params.push(ConfigParam::RewardPot(total_reward_supply.into()));
352+
}
353+
354+
if let Some(reward_parameters) = reward_parameters {
355+
params.push(ConfigParam::RewardParams(reward_parameters.into()));
341356
}
342357

343358
consensus_leader_ids
@@ -404,7 +419,8 @@ mod test {
404419
epoch_stability_depth: Arbitrary::arbitrary(g),
405420
treasury: Arbitrary::arbitrary(g),
406421
treasury_parameters: Arbitrary::arbitrary(g),
407-
rewards: Arbitrary::arbitrary(g),
422+
total_reward_supply: Arbitrary::arbitrary(g),
423+
reward_parameters: Arbitrary::arbitrary(g),
408424
}
409425
}
410426
}

jormungandr-lib/src/interfaces/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod leadership_log;
1010
mod linear_fee;
1111
mod old_address;
1212
mod ratio;
13+
mod reward_parameters;
1314
mod settings;
1415
mod stats;
1516
mod tax_type;
@@ -36,6 +37,7 @@ pub use self::leadership_log::{
3637
pub use self::linear_fee::LinearFeeDef;
3738
pub use self::old_address::OldAddress;
3839
pub use self::ratio::{ParseRatioError, Ratio};
40+
pub use self::reward_parameters::RewardParams;
3941
pub use self::settings::*;
4042
pub use self::stats::{NodeState, Stats};
4143
pub use self::tax_type::TaxType;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use crate::interfaces::Ratio;
2+
use chain_impl_mockchain::{block::Epoch, config::RewardParams as RewardParamsStd};
3+
use serde::{Deserialize, Serialize};
4+
use std::num::NonZeroU32;
5+
6+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
7+
#[serde(deny_unknown_fields, rename_all = "snake_case")]
8+
pub enum RewardParams {
9+
Linear {
10+
constant: u64,
11+
ratio: Ratio,
12+
epoch_start: Epoch,
13+
epoch_rate: NonZeroU32,
14+
},
15+
Halving {
16+
constant: u64,
17+
ratio: Ratio,
18+
epoch_start: Epoch,
19+
epoch_rate: NonZeroU32,
20+
},
21+
}
22+
23+
/* ************** Conversion *********************************** */
24+
25+
impl From<RewardParams> for RewardParamsStd {
26+
fn from(rp: RewardParams) -> Self {
27+
match rp {
28+
RewardParams::Linear {
29+
constant,
30+
ratio,
31+
epoch_start,
32+
epoch_rate,
33+
} => RewardParamsStd::Linear {
34+
constant,
35+
ratio: ratio.into(),
36+
epoch_start,
37+
epoch_rate,
38+
},
39+
RewardParams::Halving {
40+
constant,
41+
ratio,
42+
epoch_start,
43+
epoch_rate,
44+
} => RewardParamsStd::Halving {
45+
constant,
46+
ratio: ratio.into(),
47+
epoch_start,
48+
epoch_rate,
49+
},
50+
}
51+
}
52+
}
53+
54+
impl From<RewardParamsStd> for RewardParams {
55+
fn from(rp: RewardParamsStd) -> Self {
56+
match rp {
57+
RewardParamsStd::Linear {
58+
constant,
59+
ratio,
60+
epoch_start,
61+
epoch_rate,
62+
} => RewardParams::Linear {
63+
constant,
64+
ratio: ratio.into(),
65+
epoch_start,
66+
epoch_rate,
67+
},
68+
RewardParamsStd::Halving {
69+
constant,
70+
ratio,
71+
epoch_start,
72+
epoch_rate,
73+
} => RewardParams::Halving {
74+
constant,
75+
ratio: ratio.into(),
76+
epoch_start,
77+
epoch_rate,
78+
},
79+
}
80+
}
81+
}
82+
83+
#[cfg(test)]
84+
mod test {
85+
use super::*;
86+
use quickcheck::{Arbitrary, Gen, TestResult};
87+
use std::num::NonZeroU64;
88+
89+
impl Arbitrary for RewardParams {
90+
fn arbitrary<G>(g: &mut G) -> Self
91+
where
92+
G: Gen,
93+
{
94+
if bool::arbitrary(g) {
95+
Self::Linear {
96+
constant: u64::arbitrary(g),
97+
ratio: Ratio::arbitrary(g),
98+
epoch_start: Epoch::arbitrary(g),
99+
epoch_rate: NonZeroU32::new(Arbitrary::arbitrary(g))
100+
.unwrap_or(NonZeroU32::new(1).unwrap()),
101+
}
102+
} else {
103+
Self::Halving {
104+
constant: u64::arbitrary(g),
105+
ratio: Ratio::arbitrary(g),
106+
epoch_start: Epoch::arbitrary(g),
107+
epoch_rate: NonZeroU32::new(Arbitrary::arbitrary(g))
108+
.unwrap_or(NonZeroU32::new(1).unwrap()),
109+
}
110+
}
111+
}
112+
}
113+
114+
#[test]
115+
fn linear_serde_yaml() {
116+
const CONSTANT: u64 = 8170;
117+
const RATIO_NUMERATOR: u64 = 13;
118+
const RATIO_DENOMINATOR: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(19) };
119+
const EPOCH_START: Epoch = 2;
120+
const EPOCH_RATE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(5) };
121+
122+
let parameters = RewardParams::Linear {
123+
constant: CONSTANT,
124+
ratio: Ratio::new(RATIO_NUMERATOR, RATIO_DENOMINATOR),
125+
epoch_start: EPOCH_START,
126+
epoch_rate: EPOCH_RATE,
127+
};
128+
129+
assert_eq!(
130+
serde_yaml::to_string(&parameters).unwrap(),
131+
format!(
132+
"---\nlinear:\n constant: {}\n ratio: {}/{}\n epoch_start: {}\n epoch_rate: {}",
133+
CONSTANT, RATIO_NUMERATOR, RATIO_DENOMINATOR, EPOCH_START, EPOCH_RATE,
134+
)
135+
);
136+
}
137+
138+
#[test]
139+
fn halving_serde_yaml() {
140+
const CONSTANT: u64 = 8170;
141+
const RATIO_NUMERATOR: u64 = 13;
142+
const RATIO_DENOMINATOR: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(19) };
143+
const EPOCH_START: Epoch = 2;
144+
const EPOCH_RATE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(5) };
145+
146+
let parameters = RewardParams::Halving {
147+
constant: CONSTANT,
148+
ratio: Ratio::new(RATIO_NUMERATOR, RATIO_DENOMINATOR),
149+
epoch_start: EPOCH_START,
150+
epoch_rate: EPOCH_RATE,
151+
};
152+
153+
assert_eq!(
154+
serde_yaml::to_string(&parameters).unwrap(),
155+
format!(
156+
"---\nhalving:\n constant: {}\n ratio: {}/{}\n epoch_start: {}\n epoch_rate: {}",
157+
CONSTANT, RATIO_NUMERATOR, RATIO_DENOMINATOR, EPOCH_START, EPOCH_RATE,
158+
)
159+
);
160+
}
161+
162+
quickcheck! {
163+
fn value_serde_human_readable_encode_decode(value: RewardParams) -> TestResult {
164+
let s = serde_yaml::to_string(&value).unwrap();
165+
let value_dec: RewardParams = serde_yaml::from_str(&s).unwrap();
166+
167+
TestResult::from_bool(value_dec == value)
168+
}
169+
}
170+
}

0 commit comments

Comments
 (0)