common.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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::collections::HashMap;
  19. use darkfi::{tx::Transaction, util::parse::encode_base10, zk::halo2::Field};
  20. use darkfi_money_contract::{client::OwnCoin, model::TokenId};
  21. use darkfi_sdk::{
  22. crypto::{
  23. keypair::{Address, Network, PublicKey, SecretKey, StandardAddress},
  24. BaseBlind, ContractId, FuncId, DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID,
  25. },
  26. pasta::pallas,
  27. };
  28. use darkfi_serial::{deserialize, serialize};
  29. use prettytable::{format, row, Table};
  30. use crate::money::BALANCE_BASE10_DECIMALS;
  31. pub fn prettytable_addrs(
  32. network: Network,
  33. addresses: &[(u64, PublicKey, SecretKey, u64)],
  34. ) -> Table {
  35. let mut table = Table::new();
  36. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  37. table.set_titles(row!["Key ID", "Address", "Public Key", "Secret Key", "Is Default"]);
  38. for (key_id, public_key, secret_key, is_default) in addresses {
  39. let is_default = match is_default {
  40. 1 => "*",
  41. _ => "",
  42. };
  43. let address: Address = StandardAddress::from_public(network, *public_key).into();
  44. table.add_row(row![key_id, address, public_key, secret_key, is_default]);
  45. }
  46. table
  47. }
  48. pub fn prettytable_balance(
  49. balmap: &HashMap<String, u64>,
  50. alimap: &HashMap<String, String>,
  51. ) -> Table {
  52. let mut table = Table::new();
  53. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  54. table.set_titles(row!["Token ID", "Aliases", "Balance"]);
  55. for (token_id, balance) in balmap.iter() {
  56. let alias = match alimap.get(token_id) {
  57. Some(v) => v,
  58. None => "-",
  59. };
  60. table.add_row(row![token_id, alias, encode_base10(*balance, BALANCE_BASE10_DECIMALS)]);
  61. }
  62. table
  63. }
  64. pub fn prettytable_coins(
  65. coins: &[(OwnCoin, u32, bool, Option<u32>, String)],
  66. alimap: &HashMap<String, String>,
  67. ) -> Table {
  68. let mut table = Table::new();
  69. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  70. table.set_titles(row![
  71. "Coin",
  72. "Token ID",
  73. "Aliases",
  74. "Value",
  75. "Spend Hook",
  76. "User Data",
  77. "Creation Height",
  78. "Spent",
  79. "Spent Height",
  80. "Spent TX",
  81. ]);
  82. for coin in coins {
  83. let alias = match alimap.get(&coin.0.note.token_id.to_string()) {
  84. Some(v) => v,
  85. None => "-",
  86. };
  87. let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
  88. format!("{}", coin.0.note.spend_hook)
  89. } else {
  90. String::from("-")
  91. };
  92. let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
  93. bs58::encode(serialize(&coin.0.note.user_data)).into_string().to_string()
  94. } else {
  95. String::from("-")
  96. };
  97. let spent_height = match coin.3 {
  98. Some(spent_height) => spent_height.to_string(),
  99. None => String::from("-"),
  100. };
  101. table.add_row(row![
  102. bs58::encode(&serialize(&coin.0.coin.inner())).into_string().to_string(),
  103. coin.0.note.token_id,
  104. alias,
  105. format!(
  106. "{} ({})",
  107. coin.0.note.value,
  108. encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
  109. ),
  110. spend_hook,
  111. user_data,
  112. coin.1,
  113. coin.2,
  114. spent_height,
  115. coin.4,
  116. ]);
  117. }
  118. table
  119. }
  120. pub fn prettytable_tokenlist(
  121. tokens: &[(TokenId, SecretKey, BaseBlind, bool, Option<u32>)],
  122. alimap: &HashMap<String, String>,
  123. ) -> Table {
  124. let mut table = Table::new();
  125. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  126. table.set_titles(row![
  127. "Token ID",
  128. "Aliases",
  129. "Mint Authority",
  130. "Token Blind",
  131. "Frozen",
  132. "Freeze Height",
  133. ]);
  134. for (token_id, authority, blind, frozen, freeze_height) in tokens {
  135. let alias = match alimap.get(&token_id.to_string()) {
  136. Some(v) => v,
  137. None => "-",
  138. };
  139. let freeze_height = match freeze_height {
  140. Some(freeze_height) => freeze_height.to_string(),
  141. None => String::from("-"),
  142. };
  143. table.add_row(row![token_id, alias, authority, blind, frozen, freeze_height]);
  144. }
  145. table
  146. }
  147. pub fn prettytable_contract_history(deploy_history: &[(String, String, u32)]) -> Table {
  148. let mut table = Table::new();
  149. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  150. table.set_titles(row!["Transaction Hash", "Type", "Block Height"]);
  151. for (tx_hash, tx_type, block_height) in deploy_history {
  152. table.add_row(row![tx_hash, tx_type, block_height]);
  153. }
  154. table
  155. }
  156. pub fn prettytable_contract_auth(auths: &[(ContractId, SecretKey, bool, Option<u32>)]) -> Table {
  157. let mut table = Table::new();
  158. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  159. table.set_titles(row!["Contract ID", "Secret Key", "Locked", "Lock Height"]);
  160. for (contract_id, secret_key, is_locked, lock_height) in auths {
  161. let lock_height = match lock_height {
  162. Some(lock_height) => lock_height.to_string(),
  163. None => String::from("-"),
  164. };
  165. table.add_row(row![contract_id, secret_key, is_locked, lock_height]);
  166. }
  167. table
  168. }
  169. pub fn prettytable_aliases(alimap: &HashMap<String, TokenId>) -> Table {
  170. let mut table = Table::new();
  171. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  172. table.set_titles(row!["Alias", "Token ID"]);
  173. for (alias, token_id) in alimap.iter() {
  174. table.add_row(row![alias, token_id]);
  175. }
  176. table
  177. }
  178. pub fn prettytable_scanned_blocks(scanned_blocks: &[(u32, String, String)]) -> Table {
  179. let mut table = Table::new();
  180. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  181. table.set_titles(row!["Height", "Hash", "Signing Key"]);
  182. for (height, hash, signing_key) in scanned_blocks {
  183. table.add_row(row![height, hash, signing_key]);
  184. }
  185. table
  186. }
  187. pub fn pretty_tx(tx: &Transaction) -> String {
  188. let hash = tx.hash().to_string();
  189. let mut fees: Vec<String> = vec![];
  190. let mut fees_total: u64 = 0;
  191. let mut fees_overflow = false;
  192. let mut table = Table::new();
  193. table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
  194. table.add_row(row!["", "Contract", "Function"]);
  195. for (i, call) in tx.calls.iter().enumerate() {
  196. if call.data.is_money_fee() {
  197. if let Ok(fee) = deserialize(&call.data.data[1..9]) {
  198. fees.push(format!("{} DRK", encode_base10(fee, BALANCE_BASE10_DECIMALS)));
  199. fees_total = fees_total.checked_add(fee).unwrap_or_else(|| {
  200. fees_overflow = true;
  201. u64::MAX
  202. });
  203. } else {
  204. fees.push("invalid".to_string());
  205. }
  206. }
  207. let contract_name = match call.data.contract_id {
  208. id if id == *MONEY_CONTRACT_ID => "Money",
  209. id if id == *DAO_CONTRACT_ID => "DAO",
  210. id if id == *DEPLOYOOOR_CONTRACT_ID => "Deployooor",
  211. _ => "Custom",
  212. };
  213. let calldata = &call.data.data;
  214. table.add_row(row![
  215. i.to_string(),
  216. format!("{} [{}]", call.data.contract_id.to_string(), contract_name),
  217. // Function code
  218. if !calldata.is_empty() { calldata[0].to_string() } else { "-".to_string() },
  219. ]);
  220. }
  221. let fee = match fees.len() {
  222. 0 => "-".to_string(),
  223. 1 => fees[0].clone(),
  224. _ => format!(
  225. "{} [TOTAL: {}]",
  226. fees.join(", "),
  227. if fees_overflow {
  228. "OVERFLOW".to_string()
  229. } else {
  230. format!("{} DRK", encode_base10(fees_total, BALANCE_BASE10_DECIMALS))
  231. }
  232. ),
  233. };
  234. format!("Hash: {hash}\nFee: {fee}\n\n{table}")
  235. }