main.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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::{
  19. blockchain::Blockchain,
  20. consensus::{
  21. constants,
  22. leadcoin::{LeadCoin, LeadCoinSecrets},
  23. utils::fbig2base,
  24. Float10,
  25. },
  26. util::time::Timestamp,
  27. Result,
  28. };
  29. use darkfi_sdk::{
  30. crypto::MerkleTree,
  31. pasta::{group::ff::PrimeField, pallas},
  32. };
  33. use rand::Rng;
  34. // Simulation configuration
  35. const NODES: u64 = 10;
  36. const SLOTS: u64 = 10;
  37. /// PID controller configuration/constants
  38. #[derive(Clone)]
  39. struct PID {
  40. pub dt: Float10,
  41. pub _ti: Float10,
  42. pub _td: Float10,
  43. pub kp: Float10,
  44. pub ki: Float10,
  45. pub kd: Float10,
  46. pub _pid_out_step: Float10,
  47. pub max_der: Float10,
  48. pub min_der: Float10,
  49. pub max_f: Float10,
  50. pub min_f: Float10,
  51. pub deg_rate: Float10,
  52. }
  53. impl PID {
  54. fn new() -> Self {
  55. Self {
  56. dt: Float10::try_from("0.1").unwrap(),
  57. _ti: constants::FLOAT10_ONE.clone(),
  58. _td: constants::FLOAT10_ONE.clone(),
  59. kp: Float10::try_from("0.1").unwrap(),
  60. ki: Float10::try_from("0.03").unwrap(),
  61. kd: constants::FLOAT10_ONE.clone(),
  62. _pid_out_step: Float10::try_from("0.1").unwrap(),
  63. max_der: Float10::try_from("0.1").unwrap(),
  64. min_der: Float10::try_from("-0.1").unwrap(),
  65. max_f: Float10::try_from("0.99").unwrap(),
  66. min_f: Float10::try_from("0.05").unwrap(),
  67. deg_rate: Float10::try_from("0.9").unwrap(),
  68. }
  69. }
  70. }
  71. /// Node consensus state
  72. struct ConsensusState {
  73. /// Current slot
  74. pub current_slot: u64,
  75. /// Total sum of initial staking coins
  76. pub initial_distribution: u64,
  77. /// Competing coins
  78. pub coins: Vec<LeadCoin>,
  79. /// Coin commitments tree
  80. pub coins_tree: MerkleTree,
  81. /// Previous round leaders
  82. pub leaders_history: Vec<u64>,
  83. /// PID configuration
  84. pub pid: PID,
  85. }
  86. impl ConsensusState {
  87. fn pid_error(&self, feedback: Float10) -> Float10 {
  88. let target = constants::FLOAT10_ONE.clone();
  89. target - feedback
  90. }
  91. fn f_dif(&self) -> Float10 {
  92. let last_round_leader = *self.leaders_history.last().unwrap();
  93. let previous_leader = Float10::try_from(last_round_leader).unwrap();
  94. self.pid_error(previous_leader)
  95. }
  96. fn max_windowed_forks(&self) -> Float10 {
  97. let mut max = 5;
  98. let window_size = 10;
  99. let len = self.leaders_history.len();
  100. let window_beginning = if len <= (window_size + 1) { 0 } else { len - (window_size + 1) };
  101. for item in &self.leaders_history[window_beginning..] {
  102. if *item > max {
  103. max = *item;
  104. }
  105. }
  106. Float10::try_from(max).unwrap()
  107. }
  108. fn tuned_kp(&self) -> Float10 {
  109. (self.pid.kp.clone() * constants::FLOAT10_FIVE.clone()) / self.max_windowed_forks()
  110. }
  111. fn weighted_f_dif(&self) -> Float10 {
  112. self.tuned_kp() * self.f_dif()
  113. }
  114. fn f_int(&self) -> Float10 {
  115. let mut sum = constants::FLOAT10_ZERO.clone();
  116. let lead_history_len = self.leaders_history.len();
  117. let history_begin_index = if lead_history_len > 10 { lead_history_len - 10 } else { 0 };
  118. for lf in &self.leaders_history[history_begin_index..] {
  119. sum += self.pid_error(Float10::try_from(lf.clone()).unwrap()).abs();
  120. }
  121. sum
  122. }
  123. fn tuned_ki(&self) -> Float10 {
  124. (self.pid.ki.clone() * constants::FLOAT10_FIVE.clone()) / self.max_windowed_forks()
  125. }
  126. fn weighted_f_int(&self) -> Float10 {
  127. self.tuned_ki() * self.f_int()
  128. }
  129. fn f_der(&self) -> Float10 {
  130. let len = self.leaders_history.len();
  131. let last = Float10::try_from(self.leaders_history[len - 1]).unwrap();
  132. let mut der = if len > 1 {
  133. let second_to_last = Float10::try_from(self.leaders_history[len - 2]).unwrap();
  134. (self.pid_error(second_to_last) - self.pid_error(last)) / self.pid.dt.clone()
  135. } else {
  136. self.pid_error(last) / self.pid.dt.clone()
  137. };
  138. der = if der > self.pid.max_der.clone() { self.pid.max_der.clone() } else { der };
  139. der = if der < self.pid.min_der.clone() { self.pid.min_der.clone() } else { der };
  140. der
  141. }
  142. fn weighted_f_der(&self) -> Float10 {
  143. self.pid.kd.clone() * self.f_der()
  144. }
  145. fn zero_leads_len(&self) -> Float10 {
  146. let mut count = constants::FLOAT10_ZERO.clone();
  147. let hist_len = self.leaders_history.len();
  148. for i in 1..hist_len {
  149. if self.leaders_history[hist_len - i] == 0 {
  150. count += constants::FLOAT10_ONE.clone();
  151. } else {
  152. break
  153. }
  154. }
  155. count
  156. }
  157. /// Inverse probability of winning lottery having all the stake.
  158. fn win_inv_prob_with_full_stake(&self) -> Float10 {
  159. let p = self.weighted_f_dif();
  160. let i = self.weighted_f_int();
  161. let d = self.weighted_f_der();
  162. //println!("win_inv_prob_with_full_stake(): PID P: {:?}", p);
  163. //println!("win_inv_prob_with_full_stake(): PID I: {:?}", i);
  164. //println!("win_inv_prob_with_full_stake(): PID D: {:?}", d);
  165. let f = p + i.clone() + d;
  166. //println!("win_inv_prob_with_full_stake(): PID f: {}", f);
  167. if f == constants::FLOAT10_ZERO.clone() {
  168. return self.pid.min_f.clone()
  169. } else if f >= constants::FLOAT10_ONE.clone() {
  170. return self.pid.max_f.clone()
  171. }
  172. let hist_len = self.leaders_history.len();
  173. if hist_len > 3 &&
  174. self.leaders_history[hist_len - 1] == 0 &&
  175. self.leaders_history[hist_len - 2] == 0 &&
  176. self.leaders_history[hist_len - 3] == 0 &&
  177. i == constants::FLOAT10_ZERO.clone()
  178. {
  179. return f * self.pid.deg_rate.clone().powf(self.zero_leads_len())
  180. }
  181. f
  182. }
  183. /// Leadership reward, assuming constant reward
  184. /// TODO (res) implement reward mechanism with accord to DRK,DARK token-economics
  185. fn reward(&self) -> u64 {
  186. constants::REWARD
  187. }
  188. /// Network total stake, assuming constant reward.
  189. /// Only used for fine-tuning. At genesis epoch first slot, of absolute index 0,
  190. /// if no stake was distributed, the total stake would be 0.
  191. /// To avoid division by zero, we assume total stake at first division is GENESIS_TOTAL_STAKE(1).
  192. fn total_stake(&self) -> u64 {
  193. let rewards = (self.current_slot - 1) * self.reward();
  194. let total_stake = rewards + self.initial_distribution;
  195. if total_stake == 0 {
  196. return constants::GENESIS_TOTAL_STAKE
  197. }
  198. total_stake
  199. }
  200. /// Return 2-term target approximation sigma coefficients.
  201. pub fn sigmas(&self) -> (pallas::Base, pallas::Base) {
  202. let f = self.win_inv_prob_with_full_stake();
  203. let total_stake = self.total_stake();
  204. //println!("sigmas(): f: {}", f);
  205. //println!("sigmas(): stake: {}", total_stake);
  206. let one = constants::FLOAT10_ONE.clone();
  207. let two = constants::FLOAT10_TWO.clone();
  208. let field_p = Float10::try_from(constants::P).unwrap();
  209. let total_sigma = Float10::try_from(total_stake).unwrap();
  210. let x = one - f;
  211. let c = x.ln();
  212. let sigma1_fbig = c.clone() / total_sigma.clone() * field_p.clone();
  213. let sigma1 = fbig2base(sigma1_fbig);
  214. let sigma2_fbig = (c / total_sigma).powf(two.clone()) * (field_p / two);
  215. let sigma2 = fbig2base(sigma2_fbig);
  216. (sigma1, sigma2)
  217. }
  218. /// Check that the participant/stakeholder coins win the slot lottery.
  219. /// If the stakeholder has multiple competing winning coins, only the
  220. /// highest value coin is selected, since the stakeholder can't give
  221. /// more than one proof per block/slot.
  222. /// * 'sigma1', 'sigma2': slot sigmas
  223. /// Returns: (check: bool, idx: usize) where idx is the winning coin's index.
  224. pub fn is_slot_leader(&mut self, sigma1: pallas::Base, sigma2: pallas::Base) -> (bool, usize) {
  225. let mut won = false;
  226. let mut highest_stake = 0;
  227. let mut highest_stake_idx = 0;
  228. let _total_stake = self.total_stake();
  229. for (winning_idx, coin) in self.coins.iter().enumerate() {
  230. //println!("is_slot_leader: coin stake: {:?}", coin.value);
  231. //println!("is_slot_leader: total_stake: {}", total_stake);
  232. //println!("is_slot_leader: relative stake: {}", (coin.value as f64) / total_stake as f64);
  233. let first_winning = coin.is_leader(sigma1, sigma2);
  234. if first_winning && !won {
  235. highest_stake_idx = winning_idx;
  236. }
  237. won |= first_winning;
  238. if won && coin.value > highest_stake {
  239. highest_stake = coin.value;
  240. highest_stake_idx = winning_idx;
  241. }
  242. }
  243. (won, highest_stake_idx)
  244. }
  245. }
  246. /// Utility function to extract leader selection lottery randomness (eta),
  247. /// defined as the hash of the last finalized block converted to pallas::Base.
  248. fn get_eta(blockchain: &Blockchain) -> pallas::Base {
  249. let block_hash = blockchain.last().unwrap().1;
  250. let mut bytes: [u8; 32] = *block_hash.as_bytes();
  251. // We drop the last two bits of the BLAKE3 hash in order to fit it in
  252. // the pallas::Base field.
  253. bytes[30] = 0;
  254. bytes[31] = 0;
  255. pallas::Base::from_repr(bytes).unwrap()
  256. }
  257. fn generate_nodes() -> Result<Vec<ConsensusState>> {
  258. println!("Generating {NODES} nodes...");
  259. // Generate a dummy DB to get initial coins eta from genesis block hash
  260. let db = sled::Config::new().temporary(true).open()?;
  261. let timestamp = Timestamp::current_time();
  262. let blockchain = Blockchain::new(&db, timestamp, *constants::TESTNET_GENESIS_HASH_BYTES)?;
  263. // Generate coins configuration
  264. let mut stakes = vec![];
  265. let mut initial_distribution = 0;
  266. for _ in 0..NODES {
  267. let stake = rand::thread_rng().gen_range(0..1000000);
  268. initial_distribution += stake;
  269. stakes.push(stake);
  270. }
  271. let slot = 0;
  272. let eta = get_eta(&blockchain);
  273. let pid = PID::new();
  274. let mut nodes = vec![];
  275. for i in 0..NODES {
  276. println!("Generating node {i}");
  277. // Generate coin here to control stake
  278. let mut coins_tree = MerkleTree::new(constants::EPOCH_LENGTH * 100);
  279. let mut rng = rand::thread_rng();
  280. let mut seeds: Vec<u64> = Vec::with_capacity(constants::EPOCH_LENGTH);
  281. for _ in 0..constants::EPOCH_LENGTH {
  282. seeds.push(rng.gen());
  283. }
  284. let epoch_secrets = LeadCoinSecrets::generate();
  285. let coin = LeadCoin::new(
  286. eta,
  287. stakes[i as usize],
  288. slot,
  289. epoch_secrets.secret_keys[0].inner(),
  290. epoch_secrets.merkle_roots[0],
  291. 0,
  292. epoch_secrets.merkle_paths[0],
  293. pallas::Base::from(seeds[0]),
  294. &mut coins_tree,
  295. );
  296. let node_state = ConsensusState {
  297. current_slot: slot,
  298. initial_distribution,
  299. coins: vec![coin],
  300. coins_tree,
  301. leaders_history: vec![0],
  302. pid: pid.clone(),
  303. };
  304. nodes.push(node_state);
  305. }
  306. Ok(nodes)
  307. }
  308. #[async_std::main]
  309. async fn main() -> Result<()> {
  310. // This script simulates the last man standing logic of replaying the
  311. // crypsinous leader election lottery until a single leader occurs, for
  312. // instant finality. The purpose of the simulation is to validate if this
  313. // logic is feasible as the network grows.
  314. // Generate nodes
  315. let mut nodes = generate_nodes()?;
  316. // In real conditions, everyone waits until a leader arises, and then
  317. // the "draft" period begins, where other leaders can join/challenge
  318. // the fight for leadership. If a leader submits a proof after that
  319. // window passes, it gets ignored.
  320. // NOTE: This time window is the min slot time.
  321. // Playing lottery for N slots
  322. for slot in 1..SLOTS {
  323. println!("Playing lottery for slot: {slot}");
  324. // Updating nodes
  325. for node in &mut nodes {
  326. node.current_slot = slot;
  327. // Clean leaders history
  328. //node.leaders_history = vec![0];
  329. }
  330. // Start slot loop
  331. let mut slot_leader: Option<usize> = None;
  332. loop {
  333. // Check if slot leader was found
  334. if let Some(leader) = slot_leader {
  335. println!("Slot {slot} leader: {leader}");
  336. // Rewarding leader
  337. let mut coins_tree = nodes[leader].coins_tree.clone();
  338. nodes[leader].coins[0] = nodes[leader].coins[0].derive_coin(&mut coins_tree);
  339. nodes[leader].coins_tree = coins_tree;
  340. break
  341. }
  342. // Draft round where everyone plays the lottery
  343. let mut sigmas: Vec<(pallas::Base, pallas::Base)> = vec![];
  344. let mut leaders = vec![];
  345. for (i, node) in nodes.iter_mut().enumerate() {
  346. // We verify all nodes will calculate the same sigmas
  347. let (sigma1, sigma2) = node.sigmas();
  348. if sigmas.iter().any(|(s1, s2)| sigma1 != *s1 || sigma2 != *s2) {
  349. panic!("sigmas are wrong.");
  350. }
  351. sigmas.push((sigma1, sigma2));
  352. let (won, _) = node.is_slot_leader(sigma1, sigma2);
  353. if won {
  354. leaders.push(i);
  355. }
  356. }
  357. // Check if single leader was found
  358. if leaders.len() == 1 {
  359. slot_leader = Some(leaders[0]);
  360. continue
  361. }
  362. println!("Slot leaders: {:?}", leaders);
  363. // Updated nodes leaders history
  364. for node in &mut nodes {
  365. node.leaders_history.push(leaders.len() as u64);
  366. }
  367. // If more than one leader occurs, we ender the last man standing mode,
  368. // where they replay the lottery in specific time windows (rounds),
  369. // until only one is left.
  370. // Also, to "progress" to the next round, the node must have submitted
  371. // a valid proof for all the previous rounds.
  372. if leaders.len() > 1 {
  373. println!("Entering last man standing mode...");
  374. let mut round = 0;
  375. // Initially there are the leaders who have won the initial lottery.
  376. let mut survivors = leaders.clone();
  377. // Sigmas of the previous round
  378. let mut prev_sigmas = sigmas.clone();
  379. loop {
  380. println!("Round {round}, FIGHT!");
  381. // Sanity check: We verify all nodes will calculate the same
  382. // sigmas for round validations.
  383. // TODO: Something here should actually change to represent the
  384. // current round, otherwise proofs might be reusable.
  385. let mut cur_sigmas: Vec<(pallas::Base, pallas::Base)> = vec![];
  386. for node in &nodes {
  387. let (sigma1, sigma2) = node.sigmas();
  388. if prev_sigmas.iter().any(|(s1, s2)| sigma1 == *s1 && sigma2 == *s2) {
  389. panic!("the sigmas are the same like for the previous round");
  390. }
  391. if cur_sigmas.iter().any(|(s1, s2)| sigma1 != *s1 || sigma2 != *s2) {
  392. panic!("the sigmas for current round are wrong");
  393. }
  394. cur_sigmas.push((sigma1, sigma2));
  395. }
  396. // Now the lottery can be played for this round.
  397. let participants = survivors.clone();
  398. survivors = vec![];
  399. for participant in &participants {
  400. let (sigma1, sigma2) = nodes[*participant].sigmas();
  401. // Verify no shenanigans happen when recalculating sigmas
  402. if sigma1 != cur_sigmas[*participant].0 ||
  403. sigma2 != cur_sigmas[*participant].1
  404. {
  405. panic!("participant sigmas are wrong.");
  406. }
  407. let (won, _) = nodes[*participant].is_slot_leader(sigma1, sigma2);
  408. if won {
  409. survivors.push(*participant);
  410. }
  411. }
  412. // Updated nodes leaders history
  413. for node in &mut nodes {
  414. node.leaders_history.push(survivors.len() as u64);
  415. }
  416. println!("Round {round} survivors: {:?}", survivors);
  417. if survivors.is_empty() {
  418. // If nobody won this round. The same participants should play the next round.
  419. println!("Nobody won round, running new round with the same participants");
  420. survivors = participants.clone();
  421. } else if survivors.len() == 1 {
  422. println!("Node {} is the last man standing!", survivors[0]);
  423. slot_leader = Some(survivors[0]);
  424. break
  425. }
  426. round += 1;
  427. prev_sigmas = cur_sigmas.clone();
  428. }
  429. }
  430. }
  431. }
  432. Ok(())
  433. }