utils.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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 std::sync::LazyLock;
  19. use darkfi_sdk::{
  20. crypto::{DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID},
  21. tx::TransactionHash,
  22. };
  23. use num_bigint::BigUint;
  24. use randomx::{RandomXCache, RandomXFlags, RandomXVM};
  25. use tracing::info;
  26. use crate::{
  27. blockchain::{BlockInfo, BlockchainOverlayPtr, Header},
  28. runtime::vm_runtime::Runtime,
  29. validator::{
  30. consensus::{Fork, Proposal},
  31. pow::PoWModule,
  32. },
  33. Error, Result,
  34. };
  35. /// Max 32 bytes integer, used in rank calculations.
  36. /// Cached to avoid repeated allocation.
  37. pub static MAX_32_BYTES: LazyLock<BigUint> = LazyLock::new(|| BigUint::from_bytes_le(&[0xFF; 32]));
  38. /// Deploy DarkFi native wasm contracts to provided blockchain overlay.
  39. ///
  40. /// If overlay already contains the contracts, it will just open the
  41. /// necessary db and trees, and give back what it has. This means that
  42. /// on subsequent runs, our native contracts will already be in a
  43. /// deployed state, so what we actually do here is a redeployment. This
  44. /// kind of operation should only modify the contract's state in case
  45. /// it wasn't deployed before (meaning the initial run). Otherwise, it
  46. /// shouldn't touch anything, or just potentially update the db schemas
  47. /// or whatever is necessary. This logic should be handled in the init
  48. /// function of the actual contract, so make sure the native contracts
  49. /// handle this well.
  50. pub async fn deploy_native_contracts(
  51. overlay: &BlockchainOverlayPtr,
  52. block_target: u32,
  53. ) -> Result<()> {
  54. info!(target: "validator::utils::deploy_native_contracts", "Deploying native WASM contracts");
  55. // The Money contract uses an empty payload to deploy itself.
  56. let money_contract_deploy_payload = vec![];
  57. // The DAO contract uses an empty payload to deploy itself.
  58. let dao_contract_deploy_payload = vec![];
  59. // The Deployooor contract uses an empty payload to deploy itself.
  60. let deployooor_contract_deploy_payload = vec![];
  61. let native_contracts = vec![
  62. (
  63. "Money Contract",
  64. *MONEY_CONTRACT_ID,
  65. include_bytes!("../contract/money/darkfi_money_contract.wasm").to_vec(),
  66. money_contract_deploy_payload,
  67. ),
  68. (
  69. "DAO Contract",
  70. *DAO_CONTRACT_ID,
  71. include_bytes!("../contract/dao/darkfi_dao_contract.wasm").to_vec(),
  72. dao_contract_deploy_payload,
  73. ),
  74. (
  75. "Deployooor Contract",
  76. *DEPLOYOOOR_CONTRACT_ID,
  77. include_bytes!("../contract/deployooor/darkfi_deployooor_contract.wasm").to_vec(),
  78. deployooor_contract_deploy_payload,
  79. ),
  80. ];
  81. // Grab last known block height to verify against next one.
  82. // If no blocks exist, we verify against genesis block height (0).
  83. let verifying_block_height = match overlay.lock().unwrap().last() {
  84. Ok((last_block_height, _)) => last_block_height + 1,
  85. Err(_) => 0,
  86. };
  87. for (call_idx, nc) in native_contracts.into_iter().enumerate() {
  88. info!(target: "validator::utils::deploy_native_contracts", "Deploying {} with ContractID {}", nc.0, nc.1);
  89. let mut runtime = Runtime::new(
  90. &nc.2[..],
  91. overlay.clone(),
  92. nc.1,
  93. verifying_block_height,
  94. block_target,
  95. TransactionHash::none(),
  96. call_idx as u8,
  97. )?;
  98. runtime.deploy(&nc.3)?;
  99. info!(target: "validator::utils::deploy_native_contracts", "Successfully deployed {}", nc.0);
  100. }
  101. info!(target: "validator::utils::deploy_native_contracts", "Finished deployment of native WASM contracts");
  102. Ok(())
  103. }
  104. /// Verify provided header is valid for provided PoW module and compute
  105. /// its rank.
  106. /// Returns next mine difficulty, along with the computed rank.
  107. ///
  108. /// Header's rank is the tuple of its squared mining target distance
  109. /// from max 32 bytes int, along with its squared RandomX hash number
  110. /// distance from max 32 bytes int.
  111. /// Genesis block has rank (0, 0).
  112. pub fn header_rank(module: &mut PoWModule, header: &Header) -> Result<(BigUint, BigUint, BigUint)> {
  113. // Grab next mine target and difficulty
  114. let (target, difficulty) = module.next_mine_target_and_difficulty()?;
  115. // Genesis header has rank 0
  116. if header.height == 0 {
  117. return Ok((difficulty, 0u64.into(), 0u64.into()))
  118. }
  119. // Verify hash is less than the expected mine target
  120. let out_hash = module.verify_block_target(header, &target)?;
  121. // Compute the squared mining target distance
  122. let target_distance = &*MAX_32_BYTES - target;
  123. let target_distance_sq = &target_distance * &target_distance;
  124. // Compute the output hash distance
  125. let hash_distance = &*MAX_32_BYTES - out_hash;
  126. let hash_distance_sq = &hash_distance * &hash_distance;
  127. Ok((difficulty, target_distance_sq, hash_distance_sq))
  128. }
  129. /// Compute a block's rank, assuming that its valid, based on provided
  130. /// mining target.
  131. ///
  132. /// Block's rank is the tuple of its squared mining target distance
  133. /// from max 32 bytes int, along with its squared RandomX hash number
  134. /// distance from max 32 bytes int. Genesis block has rank (0, 0).
  135. pub fn block_rank(block: &BlockInfo, target: &BigUint) -> Result<(BigUint, BigUint)> {
  136. // Genesis block has rank 0
  137. if block.header.height == 0 {
  138. return Ok((0u64.into(), 0u64.into()))
  139. }
  140. // Compute the squared mining target distance
  141. let target_distance = &*MAX_32_BYTES - target;
  142. let target_distance_sq = &target_distance * &target_distance;
  143. // Setup RandomX verifier
  144. let flags = RandomXFlags::get_recommended_flags();
  145. let cache = RandomXCache::new(flags, block.header.previous.inner())?;
  146. let vm = RandomXVM::new(flags, Some(cache), None)?;
  147. // Compute the output hash distance
  148. let out_hash = vm.calculate_hash(block.hash().inner())?;
  149. let out_hash = BigUint::from_bytes_le(&out_hash);
  150. let hash_distance = &*MAX_32_BYTES - out_hash;
  151. let hash_distance_sq = &hash_distance * &hash_distance;
  152. Ok((target_distance_sq, hash_distance_sq))
  153. }
  154. /// Auxiliary function to calculate the middle value between provided
  155. /// u64 numbers.
  156. pub fn get_mid(a: u64, b: u64) -> u64 {
  157. (a / 2) + (b / 2) + ((a - 2 * (a / 2)) + (b - 2 * (b / 2))) / 2
  158. }
  159. /// Auxiliary function to calculate the median of a given `Vec<u64>`.
  160. /// The function sorts the vector internally.
  161. pub fn median(mut v: Vec<u64>) -> u64 {
  162. if v.len() == 1 {
  163. return v[0]
  164. }
  165. let n = v.len() / 2;
  166. v.sort_unstable();
  167. if v.len().is_multiple_of(2) {
  168. return get_mid(v[n - 1], v[n])
  169. }
  170. v[n]
  171. }
  172. /// Given a proposal, find the index of a fork chain it extends, along
  173. /// with the specific extended proposal index. Additionally, check that
  174. /// proposal doesn't already exists in any fork chain.
  175. pub fn find_extended_fork_index(forks: &[Fork], proposal: &Proposal) -> Result<(usize, usize)> {
  176. // Grab provided proposal hash
  177. let proposal_hash = proposal.hash;
  178. // Keep track of fork and proposal indexes
  179. let (mut fork_index, mut proposal_index) = (None, None);
  180. // Loop through all the forks
  181. for (f_index, fork) in forks.iter().enumerate() {
  182. // Traverse fork proposals sequence in reverse
  183. for (p_index, p_hash) in fork.proposals.iter().enumerate().rev() {
  184. // Check we haven't already seen that proposal
  185. if proposal_hash == *p_hash {
  186. return Err(Error::ProposalAlreadyExists)
  187. }
  188. // Check if proposal extends this fork
  189. if proposal.block.header.previous == *p_hash {
  190. (fork_index, proposal_index) = (Some(f_index), Some(p_index));
  191. }
  192. }
  193. }
  194. if let (Some(f_index), Some(p_index)) = (fork_index, proposal_index) {
  195. return Ok((f_index, p_index))
  196. }
  197. Err(Error::ExtendedChainIndexNotFound)
  198. }
  199. /// Auxiliary function to find best ranked fork.
  200. ///
  201. /// The best ranked fork is the one with the highest sum of its blocks
  202. /// squared mining target distances, from max 32 bytes int. In case of
  203. /// a tie, the fork with the highest sum of its blocks squared RandomX
  204. /// hash number distances, from max 32 bytes int, wins.
  205. pub fn best_fork_index(forks: &[Fork]) -> Result<usize> {
  206. // Check if node has any forks
  207. if forks.is_empty() {
  208. return Err(Error::ForksNotFound)
  209. }
  210. // Find the best ranked forks
  211. let mut best = &BigUint::from(0u64);
  212. let mut indexes = vec![];
  213. for (f_index, fork) in forks.iter().enumerate() {
  214. let rank = &fork.targets_rank;
  215. // Fork ranks lower that current best
  216. if rank < best {
  217. continue
  218. }
  219. // Fork has same rank as current best
  220. if rank == best {
  221. indexes.push(f_index);
  222. continue
  223. }
  224. // Fork ranks higher that current best
  225. best = rank;
  226. indexes = vec![f_index];
  227. }
  228. // If a single best ranking fork exists, return it
  229. if indexes.len() == 1 {
  230. return Ok(indexes[0])
  231. }
  232. // Break tie using their hash distances rank
  233. let mut best_index = indexes[0];
  234. for index in &indexes[1..] {
  235. if forks[*index].hashes_rank > forks[best_index].hashes_rank {
  236. best_index = *index;
  237. }
  238. }
  239. Ok(best_index)
  240. }
  241. /// Auxiliary function to find worst ranked fork.
  242. ///
  243. /// The worst ranked fork is the one with the lowest sum of its blocks
  244. /// squared mining target distances, from max 32 bytes int. In case of
  245. /// a tie, the fork with the lowest sum of its blocks squared RandomX
  246. /// hash number distances, from max 32 bytes int, wins.
  247. pub fn worst_fork_index(forks: &[Fork]) -> Result<usize> {
  248. // Check if node has any forks
  249. if forks.is_empty() {
  250. return Err(Error::ForksNotFound)
  251. }
  252. // Find the worst ranked forks
  253. let mut worst = &forks[0].targets_rank;
  254. let mut indexes = vec![0];
  255. for (f_index, fork) in forks[1..].iter().enumerate() {
  256. let rank = &fork.targets_rank;
  257. // Fork ranks higher that current worst
  258. if rank > worst {
  259. continue
  260. }
  261. // Fork has same rank as current worst
  262. if rank == worst {
  263. indexes.push(f_index + 1);
  264. continue
  265. }
  266. // Fork ranks lower that current worst
  267. worst = rank;
  268. indexes = vec![f_index + 1];
  269. }
  270. // If a single worst ranking fork exists, return it
  271. if indexes.len() == 1 {
  272. return Ok(indexes[0])
  273. }
  274. // Break tie using their hash distances rank
  275. let mut worst_index = indexes[0];
  276. for index in &indexes[1..] {
  277. if forks[*index].hashes_rank < forks[worst_index].hashes_rank {
  278. worst_index = *index;
  279. }
  280. }
  281. Ok(worst_index)
  282. }