pid.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi_sdk::{blockchain::Slot, pasta::pallas};
  19. use lazy_static::lazy_static;
  20. use log::debug;
  21. use super::float_10::{
  22. fbig2base, Float10, FLOAT10_NEG_ONE, FLOAT10_NEG_TWO, FLOAT10_ONE, FLOAT10_TWO, FLOAT10_ZERO,
  23. };
  24. /// PID controller configuration
  25. const P: &str = "28948022309329048855892746252171976963363056481941560715954676764349967630337";
  26. lazy_static! {
  27. static ref FIELD_P: Float10 = Float10::try_from(P).unwrap();
  28. static ref KP: Float10 = Float10::try_from("0.18").unwrap();
  29. static ref KI: Float10 = Float10::try_from("0.02").unwrap();
  30. static ref KD: Float10 = Float10::try_from("-0.1").unwrap();
  31. static ref MAX_F: Float10 = Float10::try_from("0.99").unwrap();
  32. static ref MIN_F: Float10 = Float10::try_from("0.01").unwrap();
  33. static ref EPSILON: Float10 = Float10::try_from("1").unwrap();
  34. // PID controller K values based on constants
  35. static ref K1: Float10 = KP.clone() + KI.clone() + KD.clone();
  36. static ref K2: Float10 = FLOAT10_NEG_ONE.clone() * KP.clone() + FLOAT10_NEG_TWO.clone() * KD.clone();
  37. static ref K3: Float10 = KD.clone();
  38. }
  39. /// Return 2-term target approximation sigma coefficients,
  40. /// alogn with the inverse probability `f` of becoming a
  41. /// block producer and the feedback error, corresponding
  42. /// to provided slot consensus state,
  43. pub fn slot_pid_output(
  44. previous_slot: &Slot,
  45. previous_producers: u64,
  46. ) -> (f64, f64, pallas::Base, pallas::Base) {
  47. let (f, error) = calculate_f(previous_slot, previous_producers);
  48. let total_tokens =
  49. Float10::try_from(previous_slot.total_tokens + previous_slot.reward).unwrap();
  50. let (sigma1, sigma2) = calculate_sigmas(f.clone(), total_tokens);
  51. (f.to_f64(), error.to_f64(), sigma1, sigma2)
  52. }
  53. /// Calculate the inverse probability `f` of becoming a block producer (winning the lottery)
  54. /// having all the tokens, and the feedback error, represented as Float10.
  55. fn calculate_f(previous_slot: &Slot, previous_producers: u64) -> (Float10, Float10) {
  56. // Convert slot values to Float10
  57. let previous_slot_f = Float10::try_from(previous_slot.pid.f).unwrap();
  58. debug!(target: "validator::consensus::pid::calculate_f", "Previous slot f: {previous_slot_f}");
  59. let previous_slot_error = Float10::try_from(previous_slot.pid.error).unwrap();
  60. debug!(target: "validator::consensus::pid::calculate_f", "Previous slot error: {previous_slot_error}");
  61. let previous_slot_previous_slot_error =
  62. Float10::try_from(previous_slot.previous.error).unwrap();
  63. debug!(target: "validator::consensus::pid::calculate_f", "Previous slot previous slot error: {previous_slot_previous_slot_error}");
  64. // Calculate feedback error based on previous block producers.
  65. let feedback = Float10::try_from(previous_producers).unwrap();
  66. debug!(target: "validator::consensus::pid::calculate_f", "Feedback: {feedback}");
  67. let error = FLOAT10_ONE.clone() - feedback;
  68. debug!(target: "validator::consensus::pid::calculate_f", "Error: {error}");
  69. // Calculate f
  70. let mut f = previous_slot_f +
  71. K1.clone() * error.clone() +
  72. K2.clone() * previous_slot_error +
  73. K3.clone() * previous_slot_previous_slot_error;
  74. debug!(target: "validator::consensus::pid::calculate_f", "Ounbounded f: {f}");
  75. // Boundaries control
  76. if f <= *FLOAT10_ZERO {
  77. f = MIN_F.clone()
  78. } else if f >= *FLOAT10_ONE {
  79. f = MAX_F.clone()
  80. }
  81. debug!(target: "validator::consensus::pid::calculate_f", "Bounded f: {f}");
  82. (f, error)
  83. }
  84. /// Return 2-term target approximation sigma coefficients,
  85. /// corresponding to provided `f` and `total_tokens` values.
  86. fn calculate_sigmas(f: Float10, total_tokens: Float10) -> (pallas::Base, pallas::Base) {
  87. // Calculate `neg_c` value
  88. let x = FLOAT10_ONE.clone() - f;
  89. let c = x.ln();
  90. let neg_c = FLOAT10_NEG_ONE.clone() * c;
  91. debug!(target: "validator::consensus::pid::calculate_sigmas", "neg_c: {neg_c}");
  92. // Calculate sigma 1
  93. let sigma1_fbig = neg_c.clone() / (total_tokens.clone() + EPSILON.clone()) * FIELD_P.clone();
  94. let sigma1 = fbig2base(sigma1_fbig);
  95. debug!(target: "validator::consensus::pid::calculate_sigmas", "Sigma 1: {sigma1:?}");
  96. // Calculate sigma 2
  97. let sigma2_fbig = (neg_c / (total_tokens + EPSILON.clone())).powf(FLOAT10_TWO.clone()) *
  98. (FIELD_P.clone() / FLOAT10_TWO.clone());
  99. let sigma2 = fbig2base(sigma2_fbig);
  100. debug!(target: "validator::consensus::pid::calculate_sigmas", "Sigma 2: {sigma2:?}");
  101. (sigma1, sigma2)
  102. }