cli_output.rs 112 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553
  1. #![allow(clippy::to_string_in_format_args)]
  2. use {
  3. crate::{
  4. cli_version::CliVersion,
  5. display::{
  6. build_balance_message, build_balance_message_with_config, format_labeled_address,
  7. unix_timestamp_to_string, writeln_name_value, writeln_transaction,
  8. BuildBalanceMessageConfig,
  9. },
  10. QuietDisplay, VerboseDisplay,
  11. },
  12. base64::{prelude::BASE64_STANDARD, Engine},
  13. chrono::{Local, TimeZone, Utc},
  14. clap::ArgMatches,
  15. console::{style, Emoji},
  16. inflector::cases::titlecase::to_title_case,
  17. serde::{Deserialize, Serialize},
  18. serde_json::{Map, Value},
  19. solana_account::ReadableAccount,
  20. solana_account_decoder::{
  21. encode_ui_account, parse_account_data::AccountAdditionalDataV3,
  22. parse_token::UiTokenAccount, UiAccountEncoding, UiDataSliceConfig,
  23. },
  24. solana_clap_utils::keypair::SignOnly,
  25. solana_clock::{Epoch, Slot, UnixTimestamp},
  26. solana_epoch_info::EpochInfo,
  27. solana_hash::Hash,
  28. solana_pubkey::Pubkey,
  29. solana_rpc_client_api::response::{
  30. RpcAccountBalance, RpcContactInfo, RpcInflationGovernor, RpcInflationRate, RpcKeyedAccount,
  31. RpcSupply, RpcVoteAccountInfo,
  32. },
  33. solana_signature::Signature,
  34. solana_stake_interface::{
  35. stake_history::StakeHistoryEntry,
  36. state::{Authorized, Lockup},
  37. },
  38. solana_transaction::{versioned::VersionedTransaction, Transaction},
  39. solana_transaction_status::{
  40. EncodedConfirmedBlock, EncodedTransaction, TransactionConfirmationStatus,
  41. UiTransactionStatusMeta,
  42. },
  43. solana_transaction_status_client_types::UiTransactionError,
  44. solana_vote_program::{
  45. authorized_voters::AuthorizedVoters,
  46. vote_state::{BlockTimestamp, LandedVote, MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY},
  47. },
  48. std::{
  49. collections::{BTreeMap, HashMap},
  50. fmt,
  51. str::FromStr,
  52. time::Duration,
  53. },
  54. };
  55. static CHECK_MARK: Emoji = Emoji("✅ ", "");
  56. static CROSS_MARK: Emoji = Emoji("❌ ", "");
  57. static WARNING: Emoji = Emoji("⚠️", "!");
  58. #[derive(Clone, Debug, PartialEq, Eq)]
  59. pub enum OutputFormat {
  60. Display,
  61. Json,
  62. JsonCompact,
  63. DisplayQuiet,
  64. DisplayVerbose,
  65. }
  66. impl OutputFormat {
  67. pub fn formatted_string<T>(&self, item: &T) -> String
  68. where
  69. T: Serialize + fmt::Display + QuietDisplay + VerboseDisplay,
  70. {
  71. match self {
  72. OutputFormat::Display => format!("{item}"),
  73. OutputFormat::DisplayQuiet => {
  74. let mut s = String::new();
  75. QuietDisplay::write_str(item, &mut s).unwrap();
  76. s
  77. }
  78. OutputFormat::DisplayVerbose => {
  79. let mut s = String::new();
  80. VerboseDisplay::write_str(item, &mut s).unwrap();
  81. s
  82. }
  83. OutputFormat::Json => serde_json::to_string_pretty(item).unwrap(),
  84. OutputFormat::JsonCompact => serde_json::to_value(item).unwrap().to_string(),
  85. }
  86. }
  87. pub fn from_matches(matches: &ArgMatches<'_>, output_name: &str, verbose: bool) -> Self {
  88. matches
  89. .value_of(output_name)
  90. .map(|value| match value {
  91. "json" => OutputFormat::Json,
  92. "json-compact" => OutputFormat::JsonCompact,
  93. _ => unreachable!(),
  94. })
  95. .unwrap_or(if verbose {
  96. OutputFormat::DisplayVerbose
  97. } else {
  98. OutputFormat::Display
  99. })
  100. }
  101. }
  102. #[derive(Serialize)]
  103. pub struct CliPrioritizationFeeStats {
  104. pub fees: Vec<CliPrioritizationFee>,
  105. pub min: u64,
  106. pub max: u64,
  107. pub average: u64,
  108. pub num_slots: u64,
  109. }
  110. impl QuietDisplay for CliPrioritizationFeeStats {}
  111. impl VerboseDisplay for CliPrioritizationFeeStats {
  112. fn write_str(&self, f: &mut dyn std::fmt::Write) -> fmt::Result {
  113. writeln!(f, "{:<11} prioritization_fee", "slot")?;
  114. for fee in &self.fees {
  115. write!(f, "{}", fee)?;
  116. }
  117. write!(f, "{}", self)
  118. }
  119. }
  120. impl fmt::Display for CliPrioritizationFeeStats {
  121. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  122. writeln!(
  123. f,
  124. "Fees in recent {} slots: Min: {} Max: {} Average: {}",
  125. self.num_slots, self.min, self.max, self.average
  126. )
  127. }
  128. }
  129. #[derive(Serialize)]
  130. pub struct CliPrioritizationFee {
  131. pub slot: Slot,
  132. pub prioritization_fee: u64,
  133. }
  134. impl QuietDisplay for CliPrioritizationFee {}
  135. impl VerboseDisplay for CliPrioritizationFee {}
  136. impl fmt::Display for CliPrioritizationFee {
  137. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  138. writeln!(f, "{:<11} {}", self.slot, self.prioritization_fee)
  139. }
  140. }
  141. #[derive(Serialize, Deserialize)]
  142. pub struct CliAccount {
  143. #[serde(flatten)]
  144. pub keyed_account: RpcKeyedAccount,
  145. #[serde(skip_serializing, skip_deserializing)]
  146. pub use_lamports_unit: bool,
  147. }
  148. pub struct CliAccountNewConfig {
  149. pub data_encoding: UiAccountEncoding,
  150. pub additional_data: Option<AccountAdditionalDataV3>,
  151. pub data_slice_config: Option<UiDataSliceConfig>,
  152. pub use_lamports_unit: bool,
  153. }
  154. impl Default for CliAccountNewConfig {
  155. fn default() -> Self {
  156. Self {
  157. data_encoding: UiAccountEncoding::Base64,
  158. additional_data: None,
  159. data_slice_config: None,
  160. use_lamports_unit: false,
  161. }
  162. }
  163. }
  164. impl CliAccount {
  165. pub fn new<T: ReadableAccount>(address: &Pubkey, account: &T, use_lamports_unit: bool) -> Self {
  166. Self::new_with_config(
  167. address,
  168. account,
  169. &CliAccountNewConfig {
  170. use_lamports_unit,
  171. ..CliAccountNewConfig::default()
  172. },
  173. )
  174. }
  175. pub fn new_with_config<T: ReadableAccount>(
  176. address: &Pubkey,
  177. account: &T,
  178. config: &CliAccountNewConfig,
  179. ) -> Self {
  180. let CliAccountNewConfig {
  181. data_encoding,
  182. additional_data,
  183. data_slice_config,
  184. use_lamports_unit,
  185. } = *config;
  186. Self {
  187. keyed_account: RpcKeyedAccount {
  188. pubkey: address.to_string(),
  189. account: encode_ui_account(
  190. address,
  191. account,
  192. data_encoding,
  193. additional_data,
  194. data_slice_config,
  195. ),
  196. },
  197. use_lamports_unit,
  198. }
  199. }
  200. }
  201. impl QuietDisplay for CliAccount {}
  202. impl VerboseDisplay for CliAccount {}
  203. impl fmt::Display for CliAccount {
  204. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  205. writeln!(f)?;
  206. writeln_name_value(f, "Public Key:", &self.keyed_account.pubkey)?;
  207. writeln_name_value(
  208. f,
  209. "Balance:",
  210. &build_balance_message(
  211. self.keyed_account.account.lamports,
  212. self.use_lamports_unit,
  213. true,
  214. ),
  215. )?;
  216. writeln_name_value(f, "Owner:", &self.keyed_account.account.owner)?;
  217. writeln_name_value(
  218. f,
  219. "Executable:",
  220. &self.keyed_account.account.executable.to_string(),
  221. )?;
  222. writeln_name_value(
  223. f,
  224. "Rent Epoch:",
  225. &self.keyed_account.account.rent_epoch.to_string(),
  226. )?;
  227. Ok(())
  228. }
  229. }
  230. #[derive(Default, Serialize, Deserialize)]
  231. pub struct CliBlockProduction {
  232. pub epoch: Epoch,
  233. pub start_slot: Slot,
  234. pub end_slot: Slot,
  235. pub total_slots: usize,
  236. pub total_blocks_produced: usize,
  237. pub total_slots_skipped: usize,
  238. pub leaders: Vec<CliBlockProductionEntry>,
  239. pub individual_slot_status: Vec<CliSlotStatus>,
  240. #[serde(skip_serializing)]
  241. pub verbose: bool,
  242. }
  243. impl QuietDisplay for CliBlockProduction {}
  244. impl VerboseDisplay for CliBlockProduction {}
  245. impl fmt::Display for CliBlockProduction {
  246. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  247. writeln!(f)?;
  248. writeln!(
  249. f,
  250. "{}",
  251. style(format!(
  252. " {:<44} {:>15} {:>15} {:>15} {:>15}",
  253. "Identity", "Leader Slots", "Blocks Produced", "Skipped Slots", "Skip Rate",
  254. ))
  255. .bold()
  256. )?;
  257. for leader in &self.leaders {
  258. writeln!(
  259. f,
  260. " {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
  261. leader.identity_pubkey,
  262. leader.leader_slots,
  263. leader.blocks_produced,
  264. leader.skipped_slots,
  265. leader.skipped_slots as f64 / leader.leader_slots as f64 * 100.
  266. )?;
  267. }
  268. writeln!(f)?;
  269. writeln!(
  270. f,
  271. " {:<44} {:>15} {:>15} {:>15} {:>22.2}%",
  272. format!("Epoch {} total:", self.epoch),
  273. self.total_slots,
  274. self.total_blocks_produced,
  275. self.total_slots_skipped,
  276. self.total_slots_skipped as f64 / self.total_slots as f64 * 100.
  277. )?;
  278. writeln!(
  279. f,
  280. " (using data from {} slots: {} to {})",
  281. self.total_slots, self.start_slot, self.end_slot
  282. )?;
  283. if self.verbose {
  284. writeln!(f)?;
  285. writeln!(f)?;
  286. writeln!(
  287. f,
  288. "{}",
  289. style(format!(" {:<15} {:<44}", "Slot", "Identity Pubkey")).bold(),
  290. )?;
  291. for status in &self.individual_slot_status {
  292. if status.skipped {
  293. writeln!(
  294. f,
  295. "{}",
  296. style(format!(
  297. " {:<15} {:<44} SKIPPED",
  298. status.slot, status.leader
  299. ))
  300. .red()
  301. )?;
  302. } else {
  303. writeln!(
  304. f,
  305. "{}",
  306. style(format!(" {:<15} {:<44}", status.slot, status.leader))
  307. )?;
  308. }
  309. }
  310. }
  311. Ok(())
  312. }
  313. }
  314. #[derive(Default, Serialize, Deserialize)]
  315. #[serde(rename_all = "camelCase")]
  316. pub struct CliBlockProductionEntry {
  317. pub identity_pubkey: String,
  318. pub leader_slots: u64,
  319. pub blocks_produced: u64,
  320. pub skipped_slots: u64,
  321. }
  322. #[derive(Default, Serialize, Deserialize)]
  323. #[serde(rename_all = "camelCase")]
  324. pub struct CliSlotStatus {
  325. pub slot: Slot,
  326. pub leader: String,
  327. pub skipped: bool,
  328. }
  329. #[derive(Serialize, Deserialize)]
  330. #[serde(rename_all = "camelCase")]
  331. pub struct CliEpochInfo {
  332. #[serde(flatten)]
  333. pub epoch_info: EpochInfo,
  334. pub epoch_completed_percent: f64,
  335. #[serde(skip)]
  336. pub average_slot_time_ms: u64,
  337. #[serde(skip)]
  338. pub start_block_time: Option<UnixTimestamp>,
  339. #[serde(skip)]
  340. pub current_block_time: Option<UnixTimestamp>,
  341. }
  342. impl QuietDisplay for CliEpochInfo {}
  343. impl VerboseDisplay for CliEpochInfo {}
  344. impl fmt::Display for CliEpochInfo {
  345. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  346. writeln!(f)?;
  347. writeln_name_value(
  348. f,
  349. "Block height:",
  350. &self.epoch_info.block_height.to_string(),
  351. )?;
  352. writeln_name_value(f, "Slot:", &self.epoch_info.absolute_slot.to_string())?;
  353. writeln_name_value(f, "Epoch:", &self.epoch_info.epoch.to_string())?;
  354. if let Some(transaction_count) = &self.epoch_info.transaction_count {
  355. writeln_name_value(f, "Transaction Count:", &transaction_count.to_string())?;
  356. }
  357. let start_slot = self.epoch_info.absolute_slot - self.epoch_info.slot_index;
  358. let end_slot = start_slot + self.epoch_info.slots_in_epoch;
  359. writeln_name_value(
  360. f,
  361. "Epoch Slot Range:",
  362. &format!("[{start_slot}..{end_slot})"),
  363. )?;
  364. writeln_name_value(
  365. f,
  366. "Epoch Completed Percent:",
  367. &format!("{:>3.3}%", self.epoch_completed_percent),
  368. )?;
  369. let remaining_slots_in_epoch = self.epoch_info.slots_in_epoch - self.epoch_info.slot_index;
  370. writeln_name_value(
  371. f,
  372. "Epoch Completed Slots:",
  373. &format!(
  374. "{}/{} ({} remaining)",
  375. self.epoch_info.slot_index,
  376. self.epoch_info.slots_in_epoch,
  377. remaining_slots_in_epoch
  378. ),
  379. )?;
  380. let (time_elapsed, annotation) = if let (Some(start_block_time), Some(current_block_time)) =
  381. (self.start_block_time, self.current_block_time)
  382. {
  383. (
  384. Duration::from_secs((current_block_time - start_block_time) as u64),
  385. None,
  386. )
  387. } else {
  388. (
  389. slot_to_duration(self.epoch_info.slot_index, self.average_slot_time_ms),
  390. Some("* estimated based on current slot durations"),
  391. )
  392. };
  393. let time_remaining = slot_to_duration(remaining_slots_in_epoch, self.average_slot_time_ms);
  394. writeln_name_value(
  395. f,
  396. "Epoch Completed Time:",
  397. &format!(
  398. "{}{}/{} ({} remaining)",
  399. humantime::format_duration(time_elapsed),
  400. if annotation.is_some() { "*" } else { "" },
  401. humantime::format_duration(time_elapsed + time_remaining),
  402. humantime::format_duration(time_remaining),
  403. ),
  404. )?;
  405. if let Some(annotation) = annotation {
  406. writeln!(f)?;
  407. writeln!(f, "{annotation}")?;
  408. }
  409. Ok(())
  410. }
  411. }
  412. fn slot_to_duration(slot: Slot, slot_time_ms: u64) -> Duration {
  413. Duration::from_secs((slot * slot_time_ms) / 1000)
  414. }
  415. #[derive(Serialize, Deserialize, Default)]
  416. #[serde(rename_all = "camelCase")]
  417. pub struct CliValidatorsStakeByVersion {
  418. pub current_validators: usize,
  419. pub delinquent_validators: usize,
  420. pub current_active_stake: u64,
  421. pub delinquent_active_stake: u64,
  422. }
  423. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
  424. pub enum CliValidatorsSortOrder {
  425. Delinquent,
  426. Commission,
  427. EpochCredits,
  428. Identity,
  429. LastVote,
  430. Root,
  431. SkipRate,
  432. Stake,
  433. VoteAccount,
  434. Version,
  435. }
  436. #[derive(Serialize, Deserialize)]
  437. #[serde(rename_all = "camelCase")]
  438. pub struct CliValidators {
  439. pub total_active_stake: u64,
  440. pub total_current_stake: u64,
  441. pub total_delinquent_stake: u64,
  442. pub validators: Vec<CliValidator>,
  443. pub average_skip_rate: f64,
  444. pub average_stake_weighted_skip_rate: f64,
  445. #[serde(skip_serializing)]
  446. pub validators_sort_order: CliValidatorsSortOrder,
  447. #[serde(skip_serializing)]
  448. pub validators_reverse_sort: bool,
  449. #[serde(skip_serializing)]
  450. pub number_validators: bool,
  451. pub stake_by_version: BTreeMap<CliVersion, CliValidatorsStakeByVersion>,
  452. #[serde(skip_serializing)]
  453. pub use_lamports_unit: bool,
  454. }
  455. impl QuietDisplay for CliValidators {}
  456. impl VerboseDisplay for CliValidators {}
  457. impl fmt::Display for CliValidators {
  458. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  459. fn write_vote_account(
  460. f: &mut fmt::Formatter,
  461. validator: &CliValidator,
  462. total_active_stake: u64,
  463. use_lamports_unit: bool,
  464. highest_last_vote: u64,
  465. highest_root: u64,
  466. ) -> fmt::Result {
  467. fn non_zero_or_dash(v: u64, max_v: u64) -> String {
  468. if v == 0 {
  469. " - ".into()
  470. } else if v == max_v {
  471. format!("{v:>9} ( 0)")
  472. } else if v > max_v.saturating_sub(100) {
  473. format!("{:>9} ({:>3})", v, -(max_v.saturating_sub(v) as isize))
  474. } else {
  475. format!("{v:>9} ")
  476. }
  477. }
  478. writeln!(
  479. f,
  480. "{} {:<44} {:<44} {:>3}% {:>14} {:>14} {:>7} {:>8} {:>7} {:>22} ({:.2}%)",
  481. if validator.delinquent {
  482. WARNING.to_string()
  483. } else {
  484. "\u{a0}".to_string()
  485. },
  486. validator.identity_pubkey,
  487. validator.vote_account_pubkey,
  488. validator.commission,
  489. non_zero_or_dash(validator.last_vote, highest_last_vote),
  490. non_zero_or_dash(validator.root_slot, highest_root),
  491. if let Some(skip_rate) = validator.skip_rate {
  492. format!("{skip_rate:.2}%")
  493. } else {
  494. "- ".to_string()
  495. },
  496. validator.epoch_credits,
  497. // convert to a string so that fill/alignment works correctly
  498. validator.version.to_string(),
  499. build_balance_message_with_config(
  500. validator.activated_stake,
  501. &BuildBalanceMessageConfig {
  502. use_lamports_unit,
  503. trim_trailing_zeros: false,
  504. ..BuildBalanceMessageConfig::default()
  505. }
  506. ),
  507. 100. * validator.activated_stake as f64 / total_active_stake as f64,
  508. )
  509. }
  510. let padding = if self.number_validators {
  511. ((self.validators.len() + 1) as f64).log10().floor() as usize + 1
  512. } else {
  513. 0
  514. };
  515. let header = style(format!(
  516. "{:padding$} {:<44} {:<38} {} {} {} {} {} {} {:>22}",
  517. " ",
  518. "Identity",
  519. "Vote Account",
  520. "Commission",
  521. "Last Vote ",
  522. "Root Slot ",
  523. "Skip Rate",
  524. "Credits",
  525. "Version",
  526. "Active Stake",
  527. padding = padding + 2
  528. ))
  529. .bold();
  530. writeln!(f, "{header}")?;
  531. let mut sorted_validators = self.validators.clone();
  532. match self.validators_sort_order {
  533. CliValidatorsSortOrder::Delinquent => {
  534. sorted_validators.sort_by_key(|a| a.delinquent);
  535. }
  536. CliValidatorsSortOrder::Commission => {
  537. sorted_validators.sort_by_key(|a| a.commission);
  538. }
  539. CliValidatorsSortOrder::EpochCredits => {
  540. sorted_validators.sort_by_key(|a| a.epoch_credits);
  541. }
  542. CliValidatorsSortOrder::Identity => {
  543. sorted_validators.sort_by(|a, b| a.identity_pubkey.cmp(&b.identity_pubkey));
  544. }
  545. CliValidatorsSortOrder::LastVote => {
  546. sorted_validators.sort_by_key(|a| a.last_vote);
  547. }
  548. CliValidatorsSortOrder::Root => {
  549. sorted_validators.sort_by_key(|a| a.root_slot);
  550. }
  551. CliValidatorsSortOrder::VoteAccount => {
  552. sorted_validators.sort_by(|a, b| a.vote_account_pubkey.cmp(&b.vote_account_pubkey));
  553. }
  554. CliValidatorsSortOrder::SkipRate => {
  555. sorted_validators.sort_by(|a, b| {
  556. use std::cmp::Ordering;
  557. match (a.skip_rate, b.skip_rate) {
  558. (None, None) => Ordering::Equal,
  559. (None, Some(_)) => Ordering::Greater,
  560. (Some(_), None) => Ordering::Less,
  561. (Some(a), Some(b)) => a.partial_cmp(&b).unwrap_or(Ordering::Equal),
  562. }
  563. });
  564. }
  565. CliValidatorsSortOrder::Stake => {
  566. sorted_validators.sort_by_key(|a| a.activated_stake);
  567. }
  568. CliValidatorsSortOrder::Version => {
  569. sorted_validators.sort_by(|a, b| {
  570. (&a.version, a.activated_stake).cmp(&(&b.version, b.activated_stake))
  571. });
  572. }
  573. }
  574. if self.validators_reverse_sort {
  575. sorted_validators.reverse();
  576. }
  577. let highest_root = sorted_validators
  578. .iter()
  579. .map(|v| v.root_slot)
  580. .max()
  581. .unwrap_or_default();
  582. let highest_last_vote = sorted_validators
  583. .iter()
  584. .map(|v| v.last_vote)
  585. .max()
  586. .unwrap_or_default();
  587. for (i, validator) in sorted_validators.iter().enumerate() {
  588. if padding > 0 {
  589. let num = if self.validators_reverse_sort {
  590. i + 1
  591. } else {
  592. sorted_validators.len() - i
  593. };
  594. write!(f, "{num:padding$} ")?;
  595. }
  596. write_vote_account(
  597. f,
  598. validator,
  599. self.total_active_stake,
  600. self.use_lamports_unit,
  601. highest_last_vote,
  602. highest_root,
  603. )?;
  604. }
  605. // The actual header has long scrolled away. Print the header once more as a footer
  606. if self.validators.len() > 100 {
  607. writeln!(f, "{header}")?;
  608. }
  609. writeln!(f)?;
  610. writeln_name_value(
  611. f,
  612. "Average Stake-Weighted Skip Rate:",
  613. &format!("{:.2}%", self.average_stake_weighted_skip_rate,),
  614. )?;
  615. writeln_name_value(
  616. f,
  617. "Average Unweighted Skip Rate: ",
  618. &format!("{:.2}%", self.average_skip_rate),
  619. )?;
  620. writeln!(f)?;
  621. writeln_name_value(
  622. f,
  623. "Active Stake:",
  624. &build_balance_message(self.total_active_stake, self.use_lamports_unit, true),
  625. )?;
  626. if self.total_delinquent_stake > 0 {
  627. writeln_name_value(
  628. f,
  629. "Current Stake:",
  630. &format!(
  631. "{} ({:0.2}%)",
  632. &build_balance_message(self.total_current_stake, self.use_lamports_unit, true),
  633. 100. * self.total_current_stake as f64 / self.total_active_stake as f64
  634. ),
  635. )?;
  636. writeln_name_value(
  637. f,
  638. "Delinquent Stake:",
  639. &format!(
  640. "{} ({:0.2}%)",
  641. &build_balance_message(
  642. self.total_delinquent_stake,
  643. self.use_lamports_unit,
  644. true
  645. ),
  646. 100. * self.total_delinquent_stake as f64 / self.total_active_stake as f64
  647. ),
  648. )?;
  649. }
  650. writeln!(f)?;
  651. writeln!(f, "{}", style("Stake By Version:").bold())?;
  652. for (version, info) in self.stake_by_version.iter().rev() {
  653. writeln!(
  654. f,
  655. "{:<7} - {:4} current validators ({:>5.2}%){}",
  656. // convert to a string so that fill/alignment works correctly
  657. version.to_string(),
  658. info.current_validators,
  659. 100. * info.current_active_stake as f64 / self.total_active_stake as f64,
  660. if info.delinquent_validators > 0 {
  661. format!(
  662. " {:3} delinquent validators ({:>5.2}%)",
  663. info.delinquent_validators,
  664. 100. * info.delinquent_active_stake as f64 / self.total_active_stake as f64
  665. )
  666. } else {
  667. "".to_string()
  668. },
  669. )?;
  670. }
  671. Ok(())
  672. }
  673. }
  674. #[derive(Serialize, Deserialize, Clone)]
  675. #[serde(rename_all = "camelCase")]
  676. pub struct CliValidator {
  677. pub identity_pubkey: String,
  678. pub vote_account_pubkey: String,
  679. pub commission: u8,
  680. pub last_vote: u64,
  681. pub root_slot: u64,
  682. pub credits: u64, // lifetime credits
  683. pub epoch_credits: u64, // credits earned in the current epoch
  684. pub activated_stake: u64,
  685. pub version: CliVersion,
  686. pub delinquent: bool,
  687. pub skip_rate: Option<f64>,
  688. }
  689. impl CliValidator {
  690. pub fn new(
  691. vote_account: &RpcVoteAccountInfo,
  692. current_epoch: Epoch,
  693. version: CliVersion,
  694. skip_rate: Option<f64>,
  695. address_labels: &HashMap<String, String>,
  696. ) -> Self {
  697. Self::_new(
  698. vote_account,
  699. current_epoch,
  700. version,
  701. skip_rate,
  702. address_labels,
  703. false,
  704. )
  705. }
  706. pub fn new_delinquent(
  707. vote_account: &RpcVoteAccountInfo,
  708. current_epoch: Epoch,
  709. version: CliVersion,
  710. skip_rate: Option<f64>,
  711. address_labels: &HashMap<String, String>,
  712. ) -> Self {
  713. Self::_new(
  714. vote_account,
  715. current_epoch,
  716. version,
  717. skip_rate,
  718. address_labels,
  719. true,
  720. )
  721. }
  722. fn _new(
  723. vote_account: &RpcVoteAccountInfo,
  724. current_epoch: Epoch,
  725. version: CliVersion,
  726. skip_rate: Option<f64>,
  727. address_labels: &HashMap<String, String>,
  728. delinquent: bool,
  729. ) -> Self {
  730. let (credits, epoch_credits) = vote_account
  731. .epoch_credits
  732. .iter()
  733. .find_map(|(epoch, credits, pre_credits)| {
  734. if *epoch == current_epoch {
  735. Some((*credits, credits.saturating_sub(*pre_credits)))
  736. } else {
  737. None
  738. }
  739. })
  740. .unwrap_or((0, 0));
  741. Self {
  742. identity_pubkey: format_labeled_address(&vote_account.node_pubkey, address_labels),
  743. vote_account_pubkey: format_labeled_address(&vote_account.vote_pubkey, address_labels),
  744. commission: vote_account.commission,
  745. last_vote: vote_account.last_vote,
  746. root_slot: vote_account.root_slot,
  747. credits,
  748. epoch_credits,
  749. activated_stake: vote_account.activated_stake,
  750. version,
  751. delinquent,
  752. skip_rate,
  753. }
  754. }
  755. }
  756. #[derive(Serialize, Deserialize)]
  757. #[serde(rename_all = "camelCase")]
  758. pub struct CliHistorySignatureVec(Vec<CliHistorySignature>);
  759. impl CliHistorySignatureVec {
  760. pub fn new(list: Vec<CliHistorySignature>) -> Self {
  761. Self(list)
  762. }
  763. }
  764. impl QuietDisplay for CliHistorySignatureVec {}
  765. impl VerboseDisplay for CliHistorySignatureVec {
  766. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  767. for signature in &self.0 {
  768. VerboseDisplay::write_str(signature, w)?;
  769. }
  770. writeln!(w, "{} transactions found", self.0.len())
  771. }
  772. }
  773. impl fmt::Display for CliHistorySignatureVec {
  774. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  775. for signature in &self.0 {
  776. write!(f, "{signature}")?;
  777. }
  778. writeln!(f, "{} transactions found", self.0.len())
  779. }
  780. }
  781. #[derive(Serialize, Deserialize, Default)]
  782. #[serde(rename_all = "camelCase")]
  783. pub struct CliHistorySignature {
  784. pub signature: String,
  785. #[serde(flatten, skip_serializing_if = "Option::is_none")]
  786. pub verbose: Option<CliHistoryVerbose>,
  787. }
  788. impl QuietDisplay for CliHistorySignature {}
  789. impl VerboseDisplay for CliHistorySignature {
  790. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  791. let verbose = self
  792. .verbose
  793. .as_ref()
  794. .expect("should have verbose.is_some()");
  795. writeln!(
  796. w,
  797. "{} [slot={} {}status={}] {}",
  798. self.signature,
  799. verbose.slot,
  800. match verbose.block_time {
  801. None => "".to_string(),
  802. Some(block_time) => format!("timestamp={} ", unix_timestamp_to_string(block_time)),
  803. },
  804. if let Some(err) = &verbose.err {
  805. format!("Failed: {err:?}")
  806. } else {
  807. match &verbose.confirmation_status {
  808. None => "Finalized".to_string(),
  809. Some(status) => format!("{status:?}"),
  810. }
  811. },
  812. verbose.memo.clone().unwrap_or_default(),
  813. )
  814. }
  815. }
  816. impl fmt::Display for CliHistorySignature {
  817. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  818. writeln!(f, "{}", self.signature)
  819. }
  820. }
  821. #[derive(Serialize, Deserialize, Default)]
  822. #[serde(rename_all = "camelCase")]
  823. pub struct CliHistoryVerbose {
  824. pub slot: Slot,
  825. pub block_time: Option<UnixTimestamp>,
  826. pub err: Option<UiTransactionError>,
  827. pub confirmation_status: Option<TransactionConfirmationStatus>,
  828. pub memo: Option<String>,
  829. }
  830. #[derive(Serialize, Deserialize)]
  831. #[serde(rename_all = "camelCase")]
  832. pub struct CliHistoryTransactionVec(Vec<CliTransactionConfirmation>);
  833. impl CliHistoryTransactionVec {
  834. pub fn new(list: Vec<CliTransactionConfirmation>) -> Self {
  835. Self(list)
  836. }
  837. }
  838. impl QuietDisplay for CliHistoryTransactionVec {}
  839. impl VerboseDisplay for CliHistoryTransactionVec {}
  840. impl fmt::Display for CliHistoryTransactionVec {
  841. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  842. for transaction in &self.0 {
  843. VerboseDisplay::write_str(transaction, f)?;
  844. writeln!(f)?;
  845. }
  846. writeln!(f, "{} transactions found", self.0.len())
  847. }
  848. }
  849. #[derive(Default, Serialize, Deserialize)]
  850. #[serde(rename_all = "camelCase")]
  851. pub struct CliNonceAccount {
  852. pub balance: u64,
  853. pub minimum_balance_for_rent_exemption: u64,
  854. pub nonce: Option<String>,
  855. pub lamports_per_signature: Option<u64>,
  856. pub authority: Option<String>,
  857. #[serde(skip_serializing)]
  858. pub use_lamports_unit: bool,
  859. }
  860. impl QuietDisplay for CliNonceAccount {}
  861. impl VerboseDisplay for CliNonceAccount {}
  862. impl fmt::Display for CliNonceAccount {
  863. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  864. writeln!(
  865. f,
  866. "Balance: {}",
  867. build_balance_message(self.balance, self.use_lamports_unit, true)
  868. )?;
  869. writeln!(
  870. f,
  871. "Minimum Balance Required: {}",
  872. build_balance_message(
  873. self.minimum_balance_for_rent_exemption,
  874. self.use_lamports_unit,
  875. true
  876. )
  877. )?;
  878. let nonce = self.nonce.as_deref().unwrap_or("uninitialized");
  879. writeln!(f, "Nonce blockhash: {nonce}")?;
  880. if let Some(fees) = self.lamports_per_signature {
  881. writeln!(f, "Fee: {fees} lamports per signature")?;
  882. } else {
  883. writeln!(f, "Fees: uninitialized")?;
  884. }
  885. let authority = self.authority.as_deref().unwrap_or("uninitialized");
  886. writeln!(f, "Authority: {authority}")
  887. }
  888. }
  889. #[derive(Serialize, Deserialize)]
  890. pub struct CliStakeVec(Vec<CliKeyedStakeState>);
  891. impl CliStakeVec {
  892. pub fn new(list: Vec<CliKeyedStakeState>) -> Self {
  893. Self(list)
  894. }
  895. }
  896. impl QuietDisplay for CliStakeVec {}
  897. impl VerboseDisplay for CliStakeVec {
  898. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  899. for state in &self.0 {
  900. writeln!(w)?;
  901. VerboseDisplay::write_str(state, w)?;
  902. }
  903. Ok(())
  904. }
  905. }
  906. impl fmt::Display for CliStakeVec {
  907. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  908. for state in &self.0 {
  909. writeln!(f)?;
  910. write!(f, "{state}")?;
  911. }
  912. Ok(())
  913. }
  914. }
  915. #[derive(Serialize, Deserialize)]
  916. #[serde(rename_all = "camelCase")]
  917. pub struct CliKeyedStakeState {
  918. pub stake_pubkey: String,
  919. #[serde(flatten)]
  920. pub stake_state: CliStakeState,
  921. }
  922. impl QuietDisplay for CliKeyedStakeState {}
  923. impl VerboseDisplay for CliKeyedStakeState {
  924. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  925. writeln!(w, "Stake Pubkey: {}", self.stake_pubkey)?;
  926. VerboseDisplay::write_str(&self.stake_state, w)
  927. }
  928. }
  929. impl fmt::Display for CliKeyedStakeState {
  930. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  931. writeln!(f, "Stake Pubkey: {}", self.stake_pubkey)?;
  932. write!(f, "{}", self.stake_state)
  933. }
  934. }
  935. #[derive(Serialize, Deserialize)]
  936. #[serde(rename_all = "camelCase")]
  937. pub struct CliEpochReward {
  938. pub epoch: Epoch,
  939. pub effective_slot: Slot,
  940. pub amount: u64, // lamports
  941. pub post_balance: u64, // lamports
  942. pub percent_change: f64,
  943. pub apr: Option<f64>,
  944. pub commission: Option<u8>,
  945. pub block_time: UnixTimestamp,
  946. }
  947. #[derive(Serialize, Deserialize)]
  948. #[serde(rename_all = "camelCase")]
  949. pub struct CliKeyedEpochReward {
  950. pub address: String,
  951. pub reward: Option<CliEpochReward>,
  952. }
  953. #[derive(Serialize, Deserialize, Default)]
  954. #[serde(rename_all = "camelCase")]
  955. pub struct CliEpochRewardsMetadata {
  956. pub epoch: Epoch,
  957. #[deprecated(
  958. since = "2.2.0",
  959. note = "Please use CliEpochReward::effective_slot per reward"
  960. )]
  961. pub effective_slot: Slot,
  962. #[deprecated(
  963. since = "2.2.0",
  964. note = "Please use CliEpochReward::block_time per reward"
  965. )]
  966. pub block_time: UnixTimestamp,
  967. }
  968. #[derive(Serialize, Deserialize)]
  969. #[serde(rename_all = "camelCase")]
  970. pub struct CliKeyedEpochRewards {
  971. #[serde(flatten, skip_serializing_if = "Option::is_none")]
  972. pub epoch_metadata: Option<CliEpochRewardsMetadata>,
  973. pub rewards: Vec<CliKeyedEpochReward>,
  974. }
  975. impl QuietDisplay for CliKeyedEpochRewards {}
  976. impl VerboseDisplay for CliKeyedEpochRewards {
  977. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  978. if self.rewards.is_empty() {
  979. writeln!(w, "No rewards found in epoch")?;
  980. return Ok(());
  981. }
  982. if let Some(metadata) = &self.epoch_metadata {
  983. writeln!(w, "Epoch: {}", metadata.epoch)?;
  984. }
  985. writeln!(w, "Epoch Rewards:")?;
  986. writeln!(
  987. w,
  988. " {:<44} {:<11} {:<23} {:<18} {:<20} {:>14} {:>7} {:>10}",
  989. "Address",
  990. "Reward Slot",
  991. "Time",
  992. "Amount",
  993. "New Balance",
  994. "Percent Change",
  995. "APR",
  996. "Commission"
  997. )?;
  998. for keyed_reward in &self.rewards {
  999. match &keyed_reward.reward {
  1000. Some(reward) => {
  1001. writeln!(
  1002. w,
  1003. " {:<44} {:<11} {:<23} ◎{:<17.9} ◎{:<19.9} {:>13.9}% {:>7} {:>10}",
  1004. keyed_reward.address,
  1005. reward.effective_slot,
  1006. Utc.timestamp_opt(reward.block_time, 0).unwrap(),
  1007. build_balance_message(reward.amount, false, false),
  1008. build_balance_message(reward.post_balance, false, false),
  1009. reward.percent_change,
  1010. reward
  1011. .apr
  1012. .map(|apr| format!("{apr:.2}%"))
  1013. .unwrap_or_default(),
  1014. reward
  1015. .commission
  1016. .map(|commission| format!("{commission}%"))
  1017. .unwrap_or_else(|| "-".to_string()),
  1018. )?;
  1019. }
  1020. None => {
  1021. writeln!(w, " {:<44} No rewards in epoch", keyed_reward.address,)?;
  1022. }
  1023. }
  1024. }
  1025. Ok(())
  1026. }
  1027. }
  1028. impl fmt::Display for CliKeyedEpochRewards {
  1029. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1030. if self.rewards.is_empty() {
  1031. writeln!(f, "No rewards found in epoch")?;
  1032. return Ok(());
  1033. }
  1034. if let Some(metadata) = &self.epoch_metadata {
  1035. writeln!(f, "Epoch: {}", metadata.epoch)?;
  1036. }
  1037. writeln!(f, "Epoch Rewards:")?;
  1038. writeln!(
  1039. f,
  1040. " {:<44} {:<18} {:<18} {:>14} {:>7} {:>10}",
  1041. "Address", "Amount", "New Balance", "Percent Change", "APR", "Commission"
  1042. )?;
  1043. for keyed_reward in &self.rewards {
  1044. match &keyed_reward.reward {
  1045. Some(reward) => {
  1046. writeln!(
  1047. f,
  1048. " {:<44} ◎{:<17.9} ◎{:<17.9} {:>13.9}% {:>7} {:>10}",
  1049. keyed_reward.address,
  1050. build_balance_message(reward.amount, false, false),
  1051. build_balance_message(reward.post_balance, false, false),
  1052. reward.percent_change,
  1053. reward
  1054. .apr
  1055. .map(|apr| format!("{apr:.2}%"))
  1056. .unwrap_or_default(),
  1057. reward
  1058. .commission
  1059. .map(|commission| format!("{commission}%"))
  1060. .unwrap_or_else(|| "-".to_string())
  1061. )?;
  1062. }
  1063. None => {
  1064. writeln!(f, " {:<44} No rewards in epoch", keyed_reward.address,)?;
  1065. }
  1066. }
  1067. }
  1068. Ok(())
  1069. }
  1070. }
  1071. fn show_votes_and_credits(
  1072. f: &mut fmt::Formatter,
  1073. votes: &[CliLandedVote],
  1074. epoch_voting_history: &[CliEpochVotingHistory],
  1075. ) -> fmt::Result {
  1076. if votes.is_empty() {
  1077. return Ok(());
  1078. }
  1079. // Existence of this should guarantee the occurrence of vote truncation
  1080. let newest_history_entry = epoch_voting_history.iter().next_back();
  1081. writeln!(
  1082. f,
  1083. "{} Votes (using {}/{} entries):",
  1084. (if newest_history_entry.is_none() {
  1085. "All"
  1086. } else {
  1087. "Recent"
  1088. }),
  1089. votes.len(),
  1090. MAX_LOCKOUT_HISTORY
  1091. )?;
  1092. for vote in votes.iter().rev() {
  1093. write!(
  1094. f,
  1095. "- slot: {} (confirmation count: {})",
  1096. vote.slot, vote.confirmation_count
  1097. )?;
  1098. if vote.latency == 0 {
  1099. writeln!(f)?;
  1100. } else {
  1101. writeln!(f, " (latency {})", vote.latency)?;
  1102. }
  1103. }
  1104. if let Some(newest) = newest_history_entry {
  1105. writeln!(
  1106. f,
  1107. "- ... (truncated {} rooted votes, which have been credited)",
  1108. newest.credits
  1109. )?;
  1110. }
  1111. if !epoch_voting_history.is_empty() {
  1112. writeln!(
  1113. f,
  1114. "{} Epoch Voting History (using {}/{} entries):",
  1115. (if epoch_voting_history.len() < MAX_EPOCH_CREDITS_HISTORY {
  1116. "All"
  1117. } else {
  1118. "Recent"
  1119. }),
  1120. epoch_voting_history.len(),
  1121. MAX_EPOCH_CREDITS_HISTORY
  1122. )?;
  1123. writeln!(
  1124. f,
  1125. "* missed credits include slots unavailable to vote on due to delinquent leaders",
  1126. )?;
  1127. }
  1128. for entry in epoch_voting_history.iter().rev() {
  1129. writeln!(
  1130. f, // tame fmt so that this will be folded like following
  1131. "- epoch: {}",
  1132. entry.epoch
  1133. )?;
  1134. writeln!(
  1135. f,
  1136. " credits range: ({}..{}]",
  1137. entry.prev_credits, entry.credits
  1138. )?;
  1139. writeln!(
  1140. f,
  1141. " credits/max credits: {}/{}",
  1142. entry.credits_earned,
  1143. entry.slots_in_epoch * u64::from(entry.max_credits_per_slot)
  1144. )?;
  1145. }
  1146. if let Some(oldest) = epoch_voting_history.iter().next() {
  1147. if oldest.prev_credits > 0 {
  1148. // Oldest entry doesn't start with 0. so history must be truncated...
  1149. // count of this combined pseudo credits range: (0..=oldest.prev_credits] like the above
  1150. // (or this is just [1..=oldest.prev_credits] for human's simpler minds)
  1151. let count = oldest.prev_credits;
  1152. writeln!(
  1153. f,
  1154. "- ... (omitting {count} past rooted votes, which have already been credited)"
  1155. )?;
  1156. }
  1157. }
  1158. Ok(())
  1159. }
  1160. enum Format {
  1161. Csv,
  1162. Human,
  1163. }
  1164. macro_rules! format_as {
  1165. ($target:expr, $fmt1:expr, $fmt2:expr, $which_fmt:expr, $($arg:tt)*) => {
  1166. match $which_fmt {
  1167. Format::Csv => {
  1168. writeln!(
  1169. $target,
  1170. $fmt1,
  1171. $($arg)*
  1172. )
  1173. },
  1174. Format::Human => {
  1175. writeln!(
  1176. $target,
  1177. $fmt2,
  1178. $($arg)*
  1179. )
  1180. }
  1181. }
  1182. };
  1183. }
  1184. fn show_epoch_rewards(
  1185. f: &mut fmt::Formatter,
  1186. epoch_rewards: &Option<Vec<CliEpochReward>>,
  1187. use_csv: bool,
  1188. ) -> fmt::Result {
  1189. if let Some(epoch_rewards) = epoch_rewards {
  1190. if epoch_rewards.is_empty() {
  1191. return Ok(());
  1192. }
  1193. writeln!(f, "Epoch Rewards:")?;
  1194. let fmt = if use_csv { Format::Csv } else { Format::Human };
  1195. format_as!(
  1196. f,
  1197. "{},{},{},{},{},{},{},{}",
  1198. " {:<6} {:<11} {:<26} {:<18} {:<18} {:>14} {:>14} {:>10}",
  1199. fmt,
  1200. "Epoch",
  1201. "Reward Slot",
  1202. "Time",
  1203. "Amount",
  1204. "New Balance",
  1205. "Percent Change",
  1206. "APR",
  1207. "Commission",
  1208. )?;
  1209. for reward in epoch_rewards {
  1210. format_as!(
  1211. f,
  1212. "{},{},{},{},{},{}%,{},{}",
  1213. " {:<6} {:<11} {:<26} ◎{:<17.11} ◎{:<17.11} {:>13.3}% {:>14} {:>10}",
  1214. fmt,
  1215. reward.epoch,
  1216. reward.effective_slot,
  1217. Utc.timestamp_opt(reward.block_time, 0).unwrap(),
  1218. build_balance_message(reward.amount, false, false),
  1219. build_balance_message(reward.post_balance, false, false),
  1220. reward.percent_change,
  1221. reward
  1222. .apr
  1223. .map(|apr| format!("{apr:.2}%"))
  1224. .unwrap_or_default(),
  1225. reward
  1226. .commission
  1227. .map(|commission| format!("{commission}%"))
  1228. .unwrap_or_else(|| "-".to_string())
  1229. )?;
  1230. }
  1231. }
  1232. Ok(())
  1233. }
  1234. #[derive(Default, Serialize, Deserialize)]
  1235. #[serde(rename_all = "camelCase")]
  1236. pub struct CliStakeState {
  1237. pub stake_type: CliStakeType,
  1238. pub account_balance: u64,
  1239. #[serde(skip_serializing_if = "Option::is_none")]
  1240. pub credits_observed: Option<u64>,
  1241. #[serde(skip_serializing_if = "Option::is_none")]
  1242. pub delegated_stake: Option<u64>,
  1243. #[serde(skip_serializing_if = "Option::is_none")]
  1244. pub delegated_vote_account_address: Option<String>,
  1245. #[serde(skip_serializing_if = "Option::is_none")]
  1246. pub activation_epoch: Option<Epoch>,
  1247. #[serde(skip_serializing_if = "Option::is_none")]
  1248. pub deactivation_epoch: Option<Epoch>,
  1249. #[serde(flatten, skip_serializing_if = "Option::is_none")]
  1250. pub authorized: Option<CliAuthorized>,
  1251. #[serde(flatten, skip_serializing_if = "Option::is_none")]
  1252. pub lockup: Option<CliLockup>,
  1253. #[serde(skip_serializing)]
  1254. pub use_lamports_unit: bool,
  1255. #[serde(skip_serializing)]
  1256. pub current_epoch: Epoch,
  1257. #[serde(skip_serializing_if = "Option::is_none")]
  1258. pub rent_exempt_reserve: Option<u64>,
  1259. #[serde(skip_serializing_if = "Option::is_none")]
  1260. pub active_stake: Option<u64>,
  1261. #[serde(skip_serializing_if = "Option::is_none")]
  1262. pub activating_stake: Option<u64>,
  1263. #[serde(skip_serializing_if = "Option::is_none")]
  1264. pub deactivating_stake: Option<u64>,
  1265. #[serde(skip_serializing_if = "Option::is_none")]
  1266. pub epoch_rewards: Option<Vec<CliEpochReward>>,
  1267. #[serde(skip_serializing)]
  1268. pub use_csv: bool,
  1269. }
  1270. impl QuietDisplay for CliStakeState {}
  1271. impl VerboseDisplay for CliStakeState {
  1272. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  1273. write!(w, "{self}")?;
  1274. if let Some(credits) = self.credits_observed {
  1275. writeln!(w, "Credits Observed: {credits}")?;
  1276. }
  1277. Ok(())
  1278. }
  1279. }
  1280. fn show_inactive_stake(
  1281. me: &CliStakeState,
  1282. f: &mut fmt::Formatter,
  1283. delegated_stake: u64,
  1284. ) -> fmt::Result {
  1285. if let Some(deactivation_epoch) = me.deactivation_epoch {
  1286. if me.current_epoch > deactivation_epoch {
  1287. let deactivating_stake = me.deactivating_stake.or(me.active_stake);
  1288. if let Some(deactivating_stake) = deactivating_stake {
  1289. writeln!(
  1290. f,
  1291. "Inactive Stake: {}",
  1292. build_balance_message(
  1293. delegated_stake - deactivating_stake,
  1294. me.use_lamports_unit,
  1295. true
  1296. ),
  1297. )?;
  1298. writeln!(
  1299. f,
  1300. "Deactivating Stake: {}",
  1301. build_balance_message(deactivating_stake, me.use_lamports_unit, true),
  1302. )?;
  1303. }
  1304. }
  1305. writeln!(
  1306. f,
  1307. "Stake deactivates starting from epoch: {deactivation_epoch}"
  1308. )?;
  1309. }
  1310. if let Some(delegated_vote_account_address) = &me.delegated_vote_account_address {
  1311. writeln!(
  1312. f,
  1313. "Delegated Vote Account Address: {delegated_vote_account_address}"
  1314. )?;
  1315. }
  1316. Ok(())
  1317. }
  1318. fn show_active_stake(
  1319. me: &CliStakeState,
  1320. f: &mut fmt::Formatter,
  1321. delegated_stake: u64,
  1322. ) -> fmt::Result {
  1323. if me
  1324. .deactivation_epoch
  1325. .map(|d| me.current_epoch <= d)
  1326. .unwrap_or(true)
  1327. {
  1328. let active_stake = me.active_stake.unwrap_or(0);
  1329. writeln!(
  1330. f,
  1331. "Active Stake: {}",
  1332. build_balance_message(active_stake, me.use_lamports_unit, true),
  1333. )?;
  1334. let activating_stake = me.activating_stake.or_else(|| {
  1335. if me.active_stake.is_none() {
  1336. Some(delegated_stake)
  1337. } else {
  1338. None
  1339. }
  1340. });
  1341. if let Some(activating_stake) = activating_stake {
  1342. writeln!(
  1343. f,
  1344. "Activating Stake: {}",
  1345. build_balance_message(activating_stake, me.use_lamports_unit, true),
  1346. )?;
  1347. writeln!(
  1348. f,
  1349. "Stake activates starting from epoch: {}",
  1350. me.activation_epoch.unwrap()
  1351. )?;
  1352. }
  1353. }
  1354. Ok(())
  1355. }
  1356. impl fmt::Display for CliStakeState {
  1357. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1358. fn show_authorized(f: &mut fmt::Formatter, authorized: &CliAuthorized) -> fmt::Result {
  1359. writeln!(f, "Stake Authority: {}", authorized.staker)?;
  1360. writeln!(f, "Withdraw Authority: {}", authorized.withdrawer)?;
  1361. Ok(())
  1362. }
  1363. fn show_lockup(f: &mut fmt::Formatter, lockup: Option<&CliLockup>) -> fmt::Result {
  1364. if let Some(lockup) = lockup {
  1365. if lockup.unix_timestamp != UnixTimestamp::default() {
  1366. writeln!(
  1367. f,
  1368. "Lockup Timestamp: {}",
  1369. unix_timestamp_to_string(lockup.unix_timestamp)
  1370. )?;
  1371. }
  1372. if lockup.epoch != Epoch::default() {
  1373. writeln!(f, "Lockup Epoch: {}", lockup.epoch)?;
  1374. }
  1375. writeln!(f, "Lockup Custodian: {}", lockup.custodian)?;
  1376. }
  1377. Ok(())
  1378. }
  1379. writeln!(
  1380. f,
  1381. "Balance: {}",
  1382. build_balance_message(self.account_balance, self.use_lamports_unit, true)
  1383. )?;
  1384. if let Some(rent_exempt_reserve) = self.rent_exempt_reserve {
  1385. writeln!(
  1386. f,
  1387. "Rent Exempt Reserve: {}",
  1388. build_balance_message(rent_exempt_reserve, self.use_lamports_unit, true)
  1389. )?;
  1390. }
  1391. match self.stake_type {
  1392. CliStakeType::RewardsPool => writeln!(f, "Stake account is a rewards pool")?,
  1393. CliStakeType::Uninitialized => writeln!(f, "Stake account is uninitialized")?,
  1394. CliStakeType::Initialized => {
  1395. writeln!(f, "Stake account is undelegated")?;
  1396. show_authorized(f, self.authorized.as_ref().unwrap())?;
  1397. show_lockup(f, self.lockup.as_ref())?;
  1398. }
  1399. CliStakeType::Stake => {
  1400. let show_delegation = {
  1401. self.active_stake.is_some()
  1402. || self.activating_stake.is_some()
  1403. || self.deactivating_stake.is_some()
  1404. || self
  1405. .deactivation_epoch
  1406. .map(|de| de > self.current_epoch)
  1407. .unwrap_or(true)
  1408. };
  1409. if show_delegation {
  1410. let delegated_stake = self.delegated_stake.unwrap();
  1411. writeln!(
  1412. f,
  1413. "Delegated Stake: {}",
  1414. build_balance_message(delegated_stake, self.use_lamports_unit, true)
  1415. )?;
  1416. show_active_stake(self, f, delegated_stake)?;
  1417. show_inactive_stake(self, f, delegated_stake)?;
  1418. } else {
  1419. writeln!(f, "Stake account is undelegated")?;
  1420. }
  1421. show_authorized(f, self.authorized.as_ref().unwrap())?;
  1422. show_lockup(f, self.lockup.as_ref())?;
  1423. show_epoch_rewards(f, &self.epoch_rewards, self.use_csv)?
  1424. }
  1425. }
  1426. Ok(())
  1427. }
  1428. }
  1429. #[derive(Serialize, Deserialize, PartialEq, Eq)]
  1430. pub enum CliStakeType {
  1431. Stake,
  1432. RewardsPool,
  1433. Uninitialized,
  1434. Initialized,
  1435. }
  1436. impl Default for CliStakeType {
  1437. fn default() -> Self {
  1438. Self::Uninitialized
  1439. }
  1440. }
  1441. #[derive(Serialize, Deserialize)]
  1442. #[serde(rename_all = "camelCase")]
  1443. pub struct CliStakeHistory {
  1444. pub entries: Vec<CliStakeHistoryEntry>,
  1445. #[serde(skip_serializing)]
  1446. pub use_lamports_unit: bool,
  1447. }
  1448. impl QuietDisplay for CliStakeHistory {}
  1449. impl VerboseDisplay for CliStakeHistory {}
  1450. impl fmt::Display for CliStakeHistory {
  1451. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1452. writeln!(f)?;
  1453. writeln!(
  1454. f,
  1455. "{}",
  1456. style(format!(
  1457. " {:<5} {:>20} {:>20} {:>20}",
  1458. "Epoch", "Effective Stake", "Activating Stake", "Deactivating Stake",
  1459. ))
  1460. .bold()
  1461. )?;
  1462. let config = BuildBalanceMessageConfig {
  1463. use_lamports_unit: self.use_lamports_unit,
  1464. show_unit: false,
  1465. trim_trailing_zeros: false,
  1466. };
  1467. for entry in &self.entries {
  1468. writeln!(
  1469. f,
  1470. " {:>5} {:>20} {:>20} {:>20} {}",
  1471. entry.epoch,
  1472. build_balance_message_with_config(entry.effective_stake, &config),
  1473. build_balance_message_with_config(entry.activating_stake, &config),
  1474. build_balance_message_with_config(entry.deactivating_stake, &config),
  1475. if self.use_lamports_unit {
  1476. "lamports"
  1477. } else {
  1478. "SOL"
  1479. }
  1480. )?;
  1481. }
  1482. Ok(())
  1483. }
  1484. }
  1485. impl From<&(Epoch, StakeHistoryEntry)> for CliStakeHistoryEntry {
  1486. fn from((epoch, entry): &(Epoch, StakeHistoryEntry)) -> Self {
  1487. Self {
  1488. epoch: *epoch,
  1489. effective_stake: entry.effective,
  1490. activating_stake: entry.activating,
  1491. deactivating_stake: entry.deactivating,
  1492. }
  1493. }
  1494. }
  1495. #[derive(Serialize, Deserialize)]
  1496. #[serde(rename_all = "camelCase")]
  1497. pub struct CliStakeHistoryEntry {
  1498. pub epoch: Epoch,
  1499. pub effective_stake: u64,
  1500. pub activating_stake: u64,
  1501. pub deactivating_stake: u64,
  1502. }
  1503. #[derive(Serialize, Deserialize)]
  1504. #[serde(rename_all = "camelCase")]
  1505. pub struct CliAuthorized {
  1506. pub staker: String,
  1507. pub withdrawer: String,
  1508. }
  1509. impl From<&Authorized> for CliAuthorized {
  1510. fn from(authorized: &Authorized) -> Self {
  1511. Self {
  1512. staker: authorized.staker.to_string(),
  1513. withdrawer: authorized.withdrawer.to_string(),
  1514. }
  1515. }
  1516. }
  1517. #[derive(Serialize, Deserialize)]
  1518. #[serde(rename_all = "camelCase")]
  1519. pub struct CliLockup {
  1520. pub unix_timestamp: UnixTimestamp,
  1521. pub epoch: Epoch,
  1522. pub custodian: String,
  1523. }
  1524. impl From<&Lockup> for CliLockup {
  1525. fn from(lockup: &Lockup) -> Self {
  1526. Self {
  1527. unix_timestamp: lockup.unix_timestamp,
  1528. epoch: lockup.epoch,
  1529. custodian: lockup.custodian.to_string(),
  1530. }
  1531. }
  1532. }
  1533. #[derive(Serialize, Deserialize)]
  1534. pub struct CliValidatorInfoVec(Vec<CliValidatorInfo>);
  1535. impl CliValidatorInfoVec {
  1536. pub fn new(list: Vec<CliValidatorInfo>) -> Self {
  1537. Self(list)
  1538. }
  1539. }
  1540. impl QuietDisplay for CliValidatorInfoVec {}
  1541. impl VerboseDisplay for CliValidatorInfoVec {}
  1542. impl fmt::Display for CliValidatorInfoVec {
  1543. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1544. if self.0.is_empty() {
  1545. writeln!(f, "No validator info accounts found")?;
  1546. }
  1547. for validator_info in &self.0 {
  1548. writeln!(f)?;
  1549. write!(f, "{validator_info}")?;
  1550. }
  1551. Ok(())
  1552. }
  1553. }
  1554. #[derive(Serialize, Deserialize)]
  1555. #[serde(rename_all = "camelCase")]
  1556. pub struct CliValidatorInfo {
  1557. pub identity_pubkey: String,
  1558. pub info_pubkey: String,
  1559. pub info: Map<String, Value>,
  1560. }
  1561. impl QuietDisplay for CliValidatorInfo {}
  1562. impl VerboseDisplay for CliValidatorInfo {}
  1563. impl fmt::Display for CliValidatorInfo {
  1564. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1565. writeln_name_value(f, "Validator Identity:", &self.identity_pubkey)?;
  1566. writeln_name_value(f, " Info Address:", &self.info_pubkey)?;
  1567. for (key, value) in self.info.iter() {
  1568. writeln_name_value(
  1569. f,
  1570. &format!(" {}:", to_title_case(key)),
  1571. value.as_str().unwrap_or("?"),
  1572. )?;
  1573. }
  1574. Ok(())
  1575. }
  1576. }
  1577. #[derive(Default, Serialize, Deserialize)]
  1578. #[serde(rename_all = "camelCase")]
  1579. pub struct CliVoteAccount {
  1580. pub account_balance: u64,
  1581. pub validator_identity: String,
  1582. #[serde(flatten)]
  1583. pub authorized_voters: CliAuthorizedVoters,
  1584. pub authorized_withdrawer: String,
  1585. pub credits: u64,
  1586. pub commission: u8,
  1587. pub root_slot: Option<Slot>,
  1588. pub recent_timestamp: BlockTimestamp,
  1589. pub votes: Vec<CliLandedVote>,
  1590. pub epoch_voting_history: Vec<CliEpochVotingHistory>,
  1591. #[serde(skip_serializing)]
  1592. pub use_lamports_unit: bool,
  1593. #[serde(skip_serializing)]
  1594. pub use_csv: bool,
  1595. #[serde(skip_serializing_if = "Option::is_none")]
  1596. pub epoch_rewards: Option<Vec<CliEpochReward>>,
  1597. }
  1598. impl QuietDisplay for CliVoteAccount {}
  1599. impl VerboseDisplay for CliVoteAccount {}
  1600. impl fmt::Display for CliVoteAccount {
  1601. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1602. writeln!(
  1603. f,
  1604. "Account Balance: {}",
  1605. build_balance_message(self.account_balance, self.use_lamports_unit, true)
  1606. )?;
  1607. writeln!(f, "Validator Identity: {}", self.validator_identity)?;
  1608. writeln!(f, "Vote Authority: {}", self.authorized_voters)?;
  1609. writeln!(f, "Withdraw Authority: {}", self.authorized_withdrawer)?;
  1610. writeln!(f, "Credits: {}", self.credits)?;
  1611. writeln!(f, "Commission: {}%", self.commission)?;
  1612. writeln!(
  1613. f,
  1614. "Root Slot: {}",
  1615. match self.root_slot {
  1616. Some(slot) => slot.to_string(),
  1617. None => "~".to_string(),
  1618. }
  1619. )?;
  1620. writeln!(
  1621. f,
  1622. "Recent Timestamp: {} from slot {}",
  1623. unix_timestamp_to_string(self.recent_timestamp.timestamp),
  1624. self.recent_timestamp.slot
  1625. )?;
  1626. show_votes_and_credits(f, &self.votes, &self.epoch_voting_history)?;
  1627. show_epoch_rewards(f, &self.epoch_rewards, self.use_csv)?;
  1628. Ok(())
  1629. }
  1630. }
  1631. #[derive(Default, Debug, Serialize, Deserialize)]
  1632. #[serde(rename_all = "camelCase")]
  1633. pub struct CliAuthorizedVoters {
  1634. authorized_voters: BTreeMap<Epoch, String>,
  1635. }
  1636. impl QuietDisplay for CliAuthorizedVoters {}
  1637. impl VerboseDisplay for CliAuthorizedVoters {}
  1638. impl fmt::Display for CliAuthorizedVoters {
  1639. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1640. if let Some((_epoch, current_authorized_voter)) = self.authorized_voters.first_key_value() {
  1641. write!(f, "{current_authorized_voter}")?;
  1642. } else {
  1643. write!(f, "None")?;
  1644. }
  1645. if self.authorized_voters.len() > 1 {
  1646. let (epoch, upcoming_authorized_voter) = self
  1647. .authorized_voters
  1648. .last_key_value()
  1649. .expect("CliAuthorizedVoters::authorized_voters.len() > 1");
  1650. writeln!(f)?;
  1651. write!(
  1652. f,
  1653. " New Vote Authority as of Epoch {epoch}: {upcoming_authorized_voter}"
  1654. )?;
  1655. }
  1656. Ok(())
  1657. }
  1658. }
  1659. impl From<&AuthorizedVoters> for CliAuthorizedVoters {
  1660. fn from(authorized_voters: &AuthorizedVoters) -> Self {
  1661. let mut voter_map: BTreeMap<Epoch, String> = BTreeMap::new();
  1662. for (epoch, voter) in authorized_voters.iter() {
  1663. voter_map.insert(*epoch, voter.to_string());
  1664. }
  1665. Self {
  1666. authorized_voters: voter_map,
  1667. }
  1668. }
  1669. }
  1670. #[derive(Serialize, Deserialize)]
  1671. #[serde(rename_all = "camelCase")]
  1672. pub struct CliEpochVotingHistory {
  1673. pub epoch: Epoch,
  1674. pub slots_in_epoch: u64,
  1675. pub credits_earned: u64,
  1676. pub credits: u64,
  1677. pub prev_credits: u64,
  1678. pub max_credits_per_slot: u8,
  1679. }
  1680. #[derive(Serialize, Deserialize)]
  1681. #[serde(rename_all = "camelCase")]
  1682. pub struct CliLandedVote {
  1683. pub latency: u8,
  1684. pub slot: Slot,
  1685. pub confirmation_count: u32,
  1686. }
  1687. impl From<&LandedVote> for CliLandedVote {
  1688. fn from(landed_vote: &LandedVote) -> Self {
  1689. Self {
  1690. latency: landed_vote.latency,
  1691. slot: landed_vote.slot(),
  1692. confirmation_count: landed_vote.confirmation_count(),
  1693. }
  1694. }
  1695. }
  1696. #[derive(Serialize, Deserialize)]
  1697. #[serde(rename_all = "camelCase")]
  1698. pub struct CliBlockTime {
  1699. pub slot: Slot,
  1700. pub timestamp: UnixTimestamp,
  1701. }
  1702. impl QuietDisplay for CliBlockTime {}
  1703. impl VerboseDisplay for CliBlockTime {}
  1704. impl fmt::Display for CliBlockTime {
  1705. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1706. writeln_name_value(f, "Block:", &self.slot.to_string())?;
  1707. writeln_name_value(f, "Date:", &unix_timestamp_to_string(self.timestamp))
  1708. }
  1709. }
  1710. #[derive(Serialize, Deserialize)]
  1711. #[serde(rename_all = "camelCase")]
  1712. pub struct CliLeaderSchedule {
  1713. pub epoch: Epoch,
  1714. pub leader_schedule_entries: Vec<CliLeaderScheduleEntry>,
  1715. }
  1716. impl QuietDisplay for CliLeaderSchedule {}
  1717. impl VerboseDisplay for CliLeaderSchedule {}
  1718. impl fmt::Display for CliLeaderSchedule {
  1719. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1720. for entry in &self.leader_schedule_entries {
  1721. writeln!(f, " {:<15} {:<44}", entry.slot, entry.leader)?;
  1722. }
  1723. Ok(())
  1724. }
  1725. }
  1726. #[derive(Serialize, Deserialize)]
  1727. #[serde(rename_all = "camelCase")]
  1728. pub struct CliLeaderScheduleEntry {
  1729. pub slot: Slot,
  1730. pub leader: String,
  1731. }
  1732. #[derive(Serialize, Deserialize)]
  1733. #[serde(rename_all = "camelCase")]
  1734. pub struct CliInflation {
  1735. pub governor: RpcInflationGovernor,
  1736. pub current_rate: RpcInflationRate,
  1737. }
  1738. impl QuietDisplay for CliInflation {}
  1739. impl VerboseDisplay for CliInflation {}
  1740. impl fmt::Display for CliInflation {
  1741. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1742. writeln!(f, "{}", style("Inflation Governor:").bold())?;
  1743. if (self.governor.initial - self.governor.terminal).abs() < f64::EPSILON {
  1744. writeln!(
  1745. f,
  1746. "Fixed rate: {:>5.2}%",
  1747. self.governor.terminal * 100.
  1748. )?;
  1749. } else {
  1750. writeln!(
  1751. f,
  1752. "Initial rate: {:>5.2}%",
  1753. self.governor.initial * 100.
  1754. )?;
  1755. writeln!(
  1756. f,
  1757. "Terminal rate: {:>5.2}%",
  1758. self.governor.terminal * 100.
  1759. )?;
  1760. writeln!(
  1761. f,
  1762. "Rate reduction per year: {:>5.2}%",
  1763. self.governor.taper * 100.
  1764. )?;
  1765. writeln!(
  1766. f,
  1767. "* Rate reduction is derived using the target slot time in genesis config"
  1768. )?;
  1769. }
  1770. if self.governor.foundation_term > 0. {
  1771. writeln!(
  1772. f,
  1773. "Foundation percentage: {:>5.2}%",
  1774. self.governor.foundation
  1775. )?;
  1776. writeln!(
  1777. f,
  1778. "Foundation term: {:.1} years",
  1779. self.governor.foundation_term
  1780. )?;
  1781. }
  1782. writeln!(
  1783. f,
  1784. "\n{}",
  1785. style(format!("Inflation for Epoch {}:", self.current_rate.epoch)).bold()
  1786. )?;
  1787. writeln!(
  1788. f,
  1789. "Total rate: {:>5.2}%",
  1790. self.current_rate.total * 100.
  1791. )?;
  1792. writeln!(
  1793. f,
  1794. "Staking rate: {:>5.2}%",
  1795. self.current_rate.validator * 100.
  1796. )?;
  1797. if self.current_rate.foundation > 0. {
  1798. writeln!(
  1799. f,
  1800. "Foundation rate: {:>5.2}%",
  1801. self.current_rate.foundation * 100.
  1802. )?;
  1803. }
  1804. Ok(())
  1805. }
  1806. }
  1807. #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq)]
  1808. #[serde(rename_all = "camelCase")]
  1809. pub struct CliSignOnlyData {
  1810. pub blockhash: String,
  1811. #[serde(skip_serializing_if = "Option::is_none")]
  1812. pub message: Option<String>,
  1813. #[serde(skip_serializing_if = "Vec::is_empty", default)]
  1814. pub signers: Vec<String>,
  1815. #[serde(skip_serializing_if = "Vec::is_empty", default)]
  1816. pub absent: Vec<String>,
  1817. #[serde(skip_serializing_if = "Vec::is_empty", default)]
  1818. pub bad_sig: Vec<String>,
  1819. }
  1820. impl QuietDisplay for CliSignOnlyData {}
  1821. impl VerboseDisplay for CliSignOnlyData {}
  1822. impl fmt::Display for CliSignOnlyData {
  1823. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1824. writeln!(f)?;
  1825. writeln_name_value(f, "Blockhash:", &self.blockhash)?;
  1826. if let Some(message) = self.message.as_ref() {
  1827. writeln_name_value(f, "Transaction Message:", message)?;
  1828. }
  1829. if !self.signers.is_empty() {
  1830. writeln!(f, "{}", style("Signers (Pubkey=Signature):").bold())?;
  1831. for signer in self.signers.iter() {
  1832. writeln!(f, " {signer}")?;
  1833. }
  1834. }
  1835. if !self.absent.is_empty() {
  1836. writeln!(f, "{}", style("Absent Signers (Pubkey):").bold())?;
  1837. for pubkey in self.absent.iter() {
  1838. writeln!(f, " {pubkey}")?;
  1839. }
  1840. }
  1841. if !self.bad_sig.is_empty() {
  1842. writeln!(f, "{}", style("Bad Signatures (Pubkey):").bold())?;
  1843. for pubkey in self.bad_sig.iter() {
  1844. writeln!(f, " {pubkey}")?;
  1845. }
  1846. }
  1847. Ok(())
  1848. }
  1849. }
  1850. #[derive(Serialize, Deserialize)]
  1851. #[serde(rename_all = "camelCase")]
  1852. pub struct CliSignature {
  1853. pub signature: String,
  1854. }
  1855. impl QuietDisplay for CliSignature {}
  1856. impl VerboseDisplay for CliSignature {}
  1857. impl fmt::Display for CliSignature {
  1858. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1859. writeln!(f)?;
  1860. writeln_name_value(f, "Signature:", &self.signature)?;
  1861. Ok(())
  1862. }
  1863. }
  1864. #[derive(Serialize, Deserialize)]
  1865. #[serde(rename_all = "camelCase")]
  1866. pub struct CliAccountBalances {
  1867. pub accounts: Vec<RpcAccountBalance>,
  1868. }
  1869. impl QuietDisplay for CliAccountBalances {}
  1870. impl VerboseDisplay for CliAccountBalances {}
  1871. impl fmt::Display for CliAccountBalances {
  1872. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1873. writeln!(
  1874. f,
  1875. "{}",
  1876. style(format!("{:<44} {}", "Address", "Balance")).bold()
  1877. )?;
  1878. for account in &self.accounts {
  1879. writeln!(
  1880. f,
  1881. "{:<44} {}",
  1882. account.address,
  1883. &format!(
  1884. "{} SOL",
  1885. build_balance_message(account.lamports, false, false)
  1886. ),
  1887. )?;
  1888. }
  1889. Ok(())
  1890. }
  1891. }
  1892. #[derive(Serialize, Deserialize)]
  1893. #[serde(rename_all = "camelCase")]
  1894. pub struct CliSupply {
  1895. pub total: u64,
  1896. pub circulating: u64,
  1897. pub non_circulating: u64,
  1898. pub non_circulating_accounts: Vec<String>,
  1899. #[serde(skip_serializing)]
  1900. pub print_accounts: bool,
  1901. }
  1902. impl From<RpcSupply> for CliSupply {
  1903. fn from(rpc_supply: RpcSupply) -> Self {
  1904. Self {
  1905. total: rpc_supply.total,
  1906. circulating: rpc_supply.circulating,
  1907. non_circulating: rpc_supply.non_circulating,
  1908. non_circulating_accounts: rpc_supply.non_circulating_accounts,
  1909. print_accounts: false,
  1910. }
  1911. }
  1912. }
  1913. impl QuietDisplay for CliSupply {}
  1914. impl VerboseDisplay for CliSupply {}
  1915. impl fmt::Display for CliSupply {
  1916. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1917. writeln_name_value(
  1918. f,
  1919. "Total:",
  1920. &format!("{} SOL", build_balance_message(self.total, false, false)),
  1921. )?;
  1922. writeln_name_value(
  1923. f,
  1924. "Circulating:",
  1925. &format!(
  1926. "{} SOL",
  1927. build_balance_message(self.circulating, false, false)
  1928. ),
  1929. )?;
  1930. writeln_name_value(
  1931. f,
  1932. "Non-Circulating:",
  1933. &format!(
  1934. "{} SOL",
  1935. build_balance_message(self.non_circulating, false, false)
  1936. ),
  1937. )?;
  1938. if self.print_accounts {
  1939. writeln!(f)?;
  1940. writeln_name_value(f, "Non-Circulating Accounts:", " ")?;
  1941. for account in &self.non_circulating_accounts {
  1942. writeln!(f, " {account}")?;
  1943. }
  1944. }
  1945. Ok(())
  1946. }
  1947. }
  1948. #[derive(Serialize, Deserialize)]
  1949. #[serde(rename_all = "camelCase")]
  1950. pub struct CliFeesInner {
  1951. pub slot: Slot,
  1952. pub blockhash: String,
  1953. pub lamports_per_signature: u64,
  1954. pub last_valid_slot: Option<Slot>,
  1955. pub last_valid_block_height: Option<Slot>,
  1956. }
  1957. impl QuietDisplay for CliFeesInner {}
  1958. impl VerboseDisplay for CliFeesInner {}
  1959. impl fmt::Display for CliFeesInner {
  1960. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1961. writeln_name_value(f, "Blockhash:", &self.blockhash)?;
  1962. writeln_name_value(
  1963. f,
  1964. "Lamports per signature:",
  1965. &self.lamports_per_signature.to_string(),
  1966. )?;
  1967. let last_valid_block_height = self
  1968. .last_valid_block_height
  1969. .map(|s| s.to_string())
  1970. .unwrap_or_default();
  1971. writeln_name_value(f, "Last valid block height:", &last_valid_block_height)
  1972. }
  1973. }
  1974. #[derive(Serialize, Deserialize)]
  1975. #[serde(rename_all = "camelCase")]
  1976. pub struct CliFees {
  1977. #[serde(flatten, skip_serializing_if = "Option::is_none")]
  1978. pub inner: Option<CliFeesInner>,
  1979. }
  1980. impl QuietDisplay for CliFees {}
  1981. impl VerboseDisplay for CliFees {}
  1982. impl fmt::Display for CliFees {
  1983. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1984. match self.inner.as_ref() {
  1985. Some(inner) => write!(f, "{inner}"),
  1986. None => write!(f, "Fees unavailable"),
  1987. }
  1988. }
  1989. }
  1990. impl CliFees {
  1991. pub fn some(
  1992. slot: Slot,
  1993. blockhash: Hash,
  1994. lamports_per_signature: u64,
  1995. last_valid_slot: Option<Slot>,
  1996. last_valid_block_height: Option<Slot>,
  1997. ) -> Self {
  1998. Self {
  1999. inner: Some(CliFeesInner {
  2000. slot,
  2001. blockhash: blockhash.to_string(),
  2002. lamports_per_signature,
  2003. last_valid_slot,
  2004. last_valid_block_height,
  2005. }),
  2006. }
  2007. }
  2008. pub fn none() -> Self {
  2009. Self { inner: None }
  2010. }
  2011. }
  2012. #[derive(Serialize, Deserialize)]
  2013. #[serde(rename_all = "camelCase")]
  2014. pub struct CliTokenAccount {
  2015. pub address: String,
  2016. #[serde(flatten)]
  2017. pub token_account: UiTokenAccount,
  2018. }
  2019. impl QuietDisplay for CliTokenAccount {}
  2020. impl VerboseDisplay for CliTokenAccount {}
  2021. impl fmt::Display for CliTokenAccount {
  2022. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2023. writeln!(f)?;
  2024. writeln_name_value(f, "Address:", &self.address)?;
  2025. let account = &self.token_account;
  2026. writeln_name_value(
  2027. f,
  2028. "Balance:",
  2029. &account.token_amount.real_number_string_trimmed(),
  2030. )?;
  2031. let mint = format!(
  2032. "{}{}",
  2033. account.mint,
  2034. if account.is_native { " (native)" } else { "" }
  2035. );
  2036. writeln_name_value(f, "Mint:", &mint)?;
  2037. writeln_name_value(f, "Owner:", &account.owner)?;
  2038. writeln_name_value(f, "State:", &format!("{:?}", account.state))?;
  2039. if let Some(delegate) = &account.delegate {
  2040. writeln!(f, "Delegation:")?;
  2041. writeln_name_value(f, " Delegate:", delegate)?;
  2042. let allowance = account.delegated_amount.as_ref().unwrap();
  2043. writeln_name_value(f, " Allowance:", &allowance.real_number_string_trimmed())?;
  2044. }
  2045. writeln_name_value(
  2046. f,
  2047. "Close authority:",
  2048. account.close_authority.as_ref().unwrap_or(&String::new()),
  2049. )?;
  2050. Ok(())
  2051. }
  2052. }
  2053. #[derive(Serialize, Deserialize)]
  2054. #[serde(rename_all = "camelCase")]
  2055. pub struct CliProgramId {
  2056. pub program_id: String,
  2057. pub signature: Option<String>,
  2058. }
  2059. impl QuietDisplay for CliProgramId {}
  2060. impl VerboseDisplay for CliProgramId {}
  2061. impl fmt::Display for CliProgramId {
  2062. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2063. writeln_name_value(f, "Program Id:", &self.program_id)?;
  2064. if let Some(ref signature) = self.signature {
  2065. writeln!(f)?;
  2066. writeln_name_value(f, "Signature:", signature)?;
  2067. }
  2068. Ok(())
  2069. }
  2070. }
  2071. #[derive(Serialize, Deserialize)]
  2072. #[serde(rename_all = "camelCase")]
  2073. pub struct CliProgramBuffer {
  2074. pub buffer: String,
  2075. }
  2076. impl QuietDisplay for CliProgramBuffer {}
  2077. impl VerboseDisplay for CliProgramBuffer {}
  2078. impl fmt::Display for CliProgramBuffer {
  2079. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2080. writeln_name_value(f, "Buffer:", &self.buffer)
  2081. }
  2082. }
  2083. #[derive(Debug, Serialize, Deserialize)]
  2084. #[serde(rename_all = "camelCase")]
  2085. pub enum CliProgramAccountType {
  2086. Buffer,
  2087. Program,
  2088. }
  2089. #[derive(Serialize, Deserialize)]
  2090. #[serde(rename_all = "camelCase")]
  2091. pub struct CliProgramAuthority {
  2092. pub authority: String,
  2093. pub account_type: CliProgramAccountType,
  2094. }
  2095. impl QuietDisplay for CliProgramAuthority {}
  2096. impl VerboseDisplay for CliProgramAuthority {}
  2097. impl fmt::Display for CliProgramAuthority {
  2098. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2099. writeln_name_value(f, "Account Type:", &format!("{:?}", self.account_type))?;
  2100. writeln_name_value(f, "Authority:", &self.authority)
  2101. }
  2102. }
  2103. #[derive(Serialize, Deserialize)]
  2104. #[serde(rename_all = "camelCase")]
  2105. pub struct CliProgram {
  2106. pub program_id: String,
  2107. pub owner: String,
  2108. pub data_len: usize,
  2109. }
  2110. impl QuietDisplay for CliProgram {}
  2111. impl VerboseDisplay for CliProgram {}
  2112. impl fmt::Display for CliProgram {
  2113. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2114. writeln!(f)?;
  2115. writeln_name_value(f, "Program Id:", &self.program_id)?;
  2116. writeln_name_value(f, "Owner:", &self.owner)?;
  2117. writeln_name_value(
  2118. f,
  2119. "Data Length:",
  2120. &format!("{:?} ({:#x?}) bytes", self.data_len, self.data_len),
  2121. )?;
  2122. Ok(())
  2123. }
  2124. }
  2125. #[derive(Serialize, Deserialize)]
  2126. #[serde(rename_all = "camelCase")]
  2127. pub struct CliProgramV4 {
  2128. pub program_id: String,
  2129. pub owner: String,
  2130. pub authority: String,
  2131. pub last_deploy_slot: u64,
  2132. pub status: String,
  2133. pub data_len: usize,
  2134. }
  2135. impl QuietDisplay for CliProgramV4 {}
  2136. impl VerboseDisplay for CliProgramV4 {}
  2137. impl fmt::Display for CliProgramV4 {
  2138. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2139. writeln!(f)?;
  2140. writeln_name_value(f, "Program Id:", &self.program_id)?;
  2141. writeln_name_value(f, "Owner:", &self.owner)?;
  2142. writeln_name_value(f, "Authority:", &self.authority)?;
  2143. writeln_name_value(
  2144. f,
  2145. "Last Deployed In Slot:",
  2146. &self.last_deploy_slot.to_string(),
  2147. )?;
  2148. writeln_name_value(f, "Status:", &self.status)?;
  2149. writeln_name_value(
  2150. f,
  2151. "Data Length:",
  2152. &format!("{:?} ({:#x?}) bytes", self.data_len, self.data_len),
  2153. )?;
  2154. Ok(())
  2155. }
  2156. }
  2157. #[derive(Serialize, Deserialize)]
  2158. #[serde(rename_all = "camelCase")]
  2159. pub struct CliProgramsV4 {
  2160. pub programs: Vec<CliProgramV4>,
  2161. }
  2162. impl QuietDisplay for CliProgramsV4 {}
  2163. impl VerboseDisplay for CliProgramsV4 {}
  2164. impl fmt::Display for CliProgramsV4 {
  2165. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2166. writeln!(f)?;
  2167. writeln!(
  2168. f,
  2169. "{}",
  2170. style(format!(
  2171. "{:<44} | {:<9} | {:<44} | {:<10}",
  2172. "Program Id", "Slot", "Authority", "Status"
  2173. ))
  2174. .bold()
  2175. )?;
  2176. for program in self.programs.iter() {
  2177. writeln!(
  2178. f,
  2179. "{}",
  2180. &format!(
  2181. "{:<44} | {:<9} | {:<44} | {:<10}",
  2182. program.program_id, program.last_deploy_slot, program.authority, program.status,
  2183. )
  2184. )?;
  2185. }
  2186. Ok(())
  2187. }
  2188. }
  2189. #[derive(Serialize, Deserialize)]
  2190. #[serde(rename_all = "camelCase")]
  2191. pub struct CliUpgradeableProgram {
  2192. pub program_id: String,
  2193. pub owner: String,
  2194. pub programdata_address: String,
  2195. pub authority: String,
  2196. pub last_deploy_slot: u64,
  2197. pub data_len: usize,
  2198. pub lamports: u64,
  2199. #[serde(skip_serializing)]
  2200. pub use_lamports_unit: bool,
  2201. }
  2202. impl QuietDisplay for CliUpgradeableProgram {}
  2203. impl VerboseDisplay for CliUpgradeableProgram {}
  2204. impl fmt::Display for CliUpgradeableProgram {
  2205. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2206. writeln!(f)?;
  2207. writeln_name_value(f, "Program Id:", &self.program_id)?;
  2208. writeln_name_value(f, "Owner:", &self.owner)?;
  2209. writeln_name_value(f, "ProgramData Address:", &self.programdata_address)?;
  2210. writeln_name_value(f, "Authority:", &self.authority)?;
  2211. writeln_name_value(
  2212. f,
  2213. "Last Deployed In Slot:",
  2214. &self.last_deploy_slot.to_string(),
  2215. )?;
  2216. writeln_name_value(
  2217. f,
  2218. "Data Length:",
  2219. &format!("{:?} ({:#x?}) bytes", self.data_len, self.data_len),
  2220. )?;
  2221. writeln_name_value(
  2222. f,
  2223. "Balance:",
  2224. &build_balance_message(self.lamports, self.use_lamports_unit, true),
  2225. )?;
  2226. Ok(())
  2227. }
  2228. }
  2229. #[derive(Serialize, Deserialize)]
  2230. #[serde(rename_all = "camelCase")]
  2231. pub struct CliUpgradeablePrograms {
  2232. pub programs: Vec<CliUpgradeableProgram>,
  2233. #[serde(skip_serializing)]
  2234. pub use_lamports_unit: bool,
  2235. }
  2236. impl QuietDisplay for CliUpgradeablePrograms {}
  2237. impl VerboseDisplay for CliUpgradeablePrograms {}
  2238. impl fmt::Display for CliUpgradeablePrograms {
  2239. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2240. writeln!(f)?;
  2241. writeln!(
  2242. f,
  2243. "{}",
  2244. style(format!(
  2245. "{:<44} | {:<9} | {:<44} | {}",
  2246. "Program Id", "Slot", "Authority", "Balance"
  2247. ))
  2248. .bold()
  2249. )?;
  2250. for program in self.programs.iter() {
  2251. writeln!(
  2252. f,
  2253. "{}",
  2254. &format!(
  2255. "{:<44} | {:<9} | {:<44} | {}",
  2256. program.program_id,
  2257. program.last_deploy_slot,
  2258. program.authority,
  2259. build_balance_message(program.lamports, self.use_lamports_unit, true)
  2260. )
  2261. )?;
  2262. }
  2263. Ok(())
  2264. }
  2265. }
  2266. #[derive(Serialize, Deserialize)]
  2267. #[serde(rename_all = "camelCase")]
  2268. pub struct CliUpgradeableProgramClosed {
  2269. pub program_id: String,
  2270. pub lamports: u64,
  2271. #[serde(skip_serializing)]
  2272. pub use_lamports_unit: bool,
  2273. }
  2274. impl QuietDisplay for CliUpgradeableProgramClosed {}
  2275. impl VerboseDisplay for CliUpgradeableProgramClosed {}
  2276. impl fmt::Display for CliUpgradeableProgramClosed {
  2277. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2278. writeln!(f)?;
  2279. writeln!(
  2280. f,
  2281. "Closed Program Id {}, {} reclaimed",
  2282. &self.program_id,
  2283. &build_balance_message(self.lamports, self.use_lamports_unit, true)
  2284. )?;
  2285. Ok(())
  2286. }
  2287. }
  2288. #[derive(Serialize, Deserialize)]
  2289. #[serde(rename_all = "camelCase")]
  2290. pub struct CliUpgradeableProgramExtended {
  2291. pub program_id: String,
  2292. pub additional_bytes: u32,
  2293. }
  2294. impl QuietDisplay for CliUpgradeableProgramExtended {}
  2295. impl VerboseDisplay for CliUpgradeableProgramExtended {}
  2296. impl fmt::Display for CliUpgradeableProgramExtended {
  2297. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2298. writeln!(f)?;
  2299. writeln!(
  2300. f,
  2301. "Extended Program Id {} by {} bytes",
  2302. &self.program_id, self.additional_bytes,
  2303. )?;
  2304. Ok(())
  2305. }
  2306. }
  2307. #[derive(Serialize, Deserialize)]
  2308. #[serde(rename_all = "camelCase")]
  2309. pub struct CliUpgradeableProgramMigrated {
  2310. pub program_id: String,
  2311. }
  2312. impl QuietDisplay for CliUpgradeableProgramMigrated {}
  2313. impl VerboseDisplay for CliUpgradeableProgramMigrated {}
  2314. impl fmt::Display for CliUpgradeableProgramMigrated {
  2315. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2316. writeln!(f)?;
  2317. writeln!(
  2318. f,
  2319. "Migrated Program Id {} from loader-v3 to loader-v4",
  2320. &self.program_id,
  2321. )?;
  2322. Ok(())
  2323. }
  2324. }
  2325. #[derive(Clone, Serialize, Deserialize)]
  2326. #[serde(rename_all = "camelCase")]
  2327. pub struct CliUpgradeableBuffer {
  2328. pub address: String,
  2329. pub authority: String,
  2330. pub data_len: usize,
  2331. pub lamports: u64,
  2332. #[serde(skip_serializing)]
  2333. pub use_lamports_unit: bool,
  2334. }
  2335. impl QuietDisplay for CliUpgradeableBuffer {}
  2336. impl VerboseDisplay for CliUpgradeableBuffer {}
  2337. impl fmt::Display for CliUpgradeableBuffer {
  2338. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2339. writeln!(f)?;
  2340. writeln_name_value(f, "Buffer Address:", &self.address)?;
  2341. writeln_name_value(f, "Authority:", &self.authority)?;
  2342. writeln_name_value(
  2343. f,
  2344. "Balance:",
  2345. &build_balance_message(self.lamports, self.use_lamports_unit, true),
  2346. )?;
  2347. writeln_name_value(
  2348. f,
  2349. "Data Length:",
  2350. &format!("{:?} ({:#x?}) bytes", self.data_len, self.data_len),
  2351. )?;
  2352. Ok(())
  2353. }
  2354. }
  2355. #[derive(Serialize, Deserialize)]
  2356. #[serde(rename_all = "camelCase")]
  2357. pub struct CliUpgradeableBuffers {
  2358. pub buffers: Vec<CliUpgradeableBuffer>,
  2359. #[serde(skip_serializing)]
  2360. pub use_lamports_unit: bool,
  2361. }
  2362. impl QuietDisplay for CliUpgradeableBuffers {}
  2363. impl VerboseDisplay for CliUpgradeableBuffers {}
  2364. impl fmt::Display for CliUpgradeableBuffers {
  2365. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2366. writeln!(f)?;
  2367. writeln!(
  2368. f,
  2369. "{}",
  2370. style(format!(
  2371. "{:<44} | {:<44} | {}",
  2372. "Buffer Address", "Authority", "Balance"
  2373. ))
  2374. .bold()
  2375. )?;
  2376. for buffer in self.buffers.iter() {
  2377. writeln!(
  2378. f,
  2379. "{}",
  2380. &format!(
  2381. "{:<44} | {:<44} | {}",
  2382. buffer.address,
  2383. buffer.authority,
  2384. build_balance_message(buffer.lamports, self.use_lamports_unit, true)
  2385. )
  2386. )?;
  2387. }
  2388. Ok(())
  2389. }
  2390. }
  2391. #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
  2392. #[serde(rename_all = "camelCase")]
  2393. pub struct CliAddressLookupTable {
  2394. pub lookup_table_address: String,
  2395. pub authority: Option<String>,
  2396. pub deactivation_slot: u64,
  2397. pub last_extended_slot: u64,
  2398. pub addresses: Vec<String>,
  2399. }
  2400. impl QuietDisplay for CliAddressLookupTable {}
  2401. impl VerboseDisplay for CliAddressLookupTable {}
  2402. impl fmt::Display for CliAddressLookupTable {
  2403. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2404. writeln!(f)?;
  2405. writeln_name_value(f, "Lookup Table Address:", &self.lookup_table_address)?;
  2406. if let Some(authority) = &self.authority {
  2407. writeln_name_value(f, "Authority:", authority)?;
  2408. } else {
  2409. writeln_name_value(f, "Authority:", "None (frozen)")?;
  2410. }
  2411. if self.deactivation_slot == u64::MAX {
  2412. writeln_name_value(f, "Deactivation Slot:", "None (still active)")?;
  2413. } else {
  2414. writeln_name_value(f, "Deactivation Slot:", &self.deactivation_slot.to_string())?;
  2415. }
  2416. if self.last_extended_slot == 0 {
  2417. writeln_name_value(f, "Last Extended Slot:", "None (empty)")?;
  2418. } else {
  2419. writeln_name_value(
  2420. f,
  2421. "Last Extended Slot:",
  2422. &self.last_extended_slot.to_string(),
  2423. )?;
  2424. }
  2425. if self.addresses.is_empty() {
  2426. writeln_name_value(f, "Address Table Entries:", "None (empty)")?;
  2427. } else {
  2428. writeln!(f, "{}", style("Address Table Entries:".to_string()).bold())?;
  2429. writeln!(f)?;
  2430. writeln!(
  2431. f,
  2432. "{}",
  2433. style(format!(" {:<5} {}", "Index", "Address")).bold()
  2434. )?;
  2435. for (index, address) in self.addresses.iter().enumerate() {
  2436. writeln!(f, " {index:<5} {address}")?;
  2437. }
  2438. }
  2439. Ok(())
  2440. }
  2441. }
  2442. #[derive(Serialize, Deserialize)]
  2443. #[serde(rename_all = "camelCase")]
  2444. pub struct CliAddressLookupTableCreated {
  2445. pub lookup_table_address: String,
  2446. pub signature: String,
  2447. }
  2448. impl QuietDisplay for CliAddressLookupTableCreated {}
  2449. impl VerboseDisplay for CliAddressLookupTableCreated {}
  2450. impl fmt::Display for CliAddressLookupTableCreated {
  2451. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2452. writeln!(f)?;
  2453. writeln_name_value(f, "Signature:", &self.signature)?;
  2454. writeln_name_value(f, "Lookup Table Address:", &self.lookup_table_address)?;
  2455. Ok(())
  2456. }
  2457. }
  2458. #[derive(Debug, Default)]
  2459. pub struct ReturnSignersConfig {
  2460. pub dump_transaction_message: bool,
  2461. }
  2462. pub fn return_signers(
  2463. tx: &Transaction,
  2464. output_format: &OutputFormat,
  2465. ) -> Result<String, Box<dyn std::error::Error>> {
  2466. return_signers_with_config(tx, output_format, &ReturnSignersConfig::default())
  2467. }
  2468. pub fn return_signers_with_config(
  2469. tx: &Transaction,
  2470. output_format: &OutputFormat,
  2471. config: &ReturnSignersConfig,
  2472. ) -> Result<String, Box<dyn std::error::Error>> {
  2473. let cli_command = return_signers_data(tx, config);
  2474. Ok(output_format.formatted_string(&cli_command))
  2475. }
  2476. pub fn return_signers_data(tx: &Transaction, config: &ReturnSignersConfig) -> CliSignOnlyData {
  2477. let verify_results = tx.verify_with_results();
  2478. let mut signers = Vec::new();
  2479. let mut absent = Vec::new();
  2480. let mut bad_sig = Vec::new();
  2481. tx.signatures
  2482. .iter()
  2483. .zip(tx.message.account_keys.iter())
  2484. .zip(verify_results)
  2485. .for_each(|((sig, key), res)| {
  2486. if res {
  2487. signers.push(format!("{key}={sig}"))
  2488. } else if *sig == Signature::default() {
  2489. absent.push(key.to_string());
  2490. } else {
  2491. bad_sig.push(key.to_string());
  2492. }
  2493. });
  2494. let message = if config.dump_transaction_message {
  2495. let message_data = tx.message_data();
  2496. Some(BASE64_STANDARD.encode(message_data))
  2497. } else {
  2498. None
  2499. };
  2500. CliSignOnlyData {
  2501. blockhash: tx.message.recent_blockhash.to_string(),
  2502. message,
  2503. signers,
  2504. absent,
  2505. bad_sig,
  2506. }
  2507. }
  2508. pub fn parse_sign_only_reply_string(reply: &str) -> SignOnly {
  2509. let object: Value = serde_json::from_str(reply).unwrap();
  2510. let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
  2511. let blockhash = blockhash_str.parse::<Hash>().unwrap();
  2512. let mut present_signers: Vec<(Pubkey, Signature)> = Vec::new();
  2513. let signer_strings = object.get("signers");
  2514. if let Some(sig_strings) = signer_strings {
  2515. present_signers = sig_strings
  2516. .as_array()
  2517. .unwrap()
  2518. .iter()
  2519. .map(|signer_string| {
  2520. let mut signer = signer_string.as_str().unwrap().split('=');
  2521. let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
  2522. let sig = Signature::from_str(signer.next().unwrap()).unwrap();
  2523. (key, sig)
  2524. })
  2525. .collect();
  2526. }
  2527. let mut absent_signers: Vec<Pubkey> = Vec::new();
  2528. let signer_strings = object.get("absent");
  2529. if let Some(sig_strings) = signer_strings {
  2530. absent_signers = sig_strings
  2531. .as_array()
  2532. .unwrap()
  2533. .iter()
  2534. .map(|val| {
  2535. let s = val.as_str().unwrap();
  2536. Pubkey::from_str(s).unwrap()
  2537. })
  2538. .collect();
  2539. }
  2540. let mut bad_signers: Vec<Pubkey> = Vec::new();
  2541. let signer_strings = object.get("badSig");
  2542. if let Some(sig_strings) = signer_strings {
  2543. bad_signers = sig_strings
  2544. .as_array()
  2545. .unwrap()
  2546. .iter()
  2547. .map(|val| {
  2548. let s = val.as_str().unwrap();
  2549. Pubkey::from_str(s).unwrap()
  2550. })
  2551. .collect();
  2552. }
  2553. let message = object
  2554. .get("message")
  2555. .and_then(|o| o.as_str())
  2556. .map(|m| m.to_string());
  2557. SignOnly {
  2558. blockhash,
  2559. message,
  2560. present_signers,
  2561. absent_signers,
  2562. bad_signers,
  2563. }
  2564. }
  2565. #[derive(Debug, Serialize, Deserialize)]
  2566. #[serde(rename_all = "camelCase")]
  2567. pub enum CliSignatureVerificationStatus {
  2568. None,
  2569. Pass,
  2570. Fail,
  2571. }
  2572. impl CliSignatureVerificationStatus {
  2573. pub fn verify_transaction(tx: &VersionedTransaction) -> Vec<Self> {
  2574. tx.verify_with_results()
  2575. .iter()
  2576. .zip(&tx.signatures)
  2577. .map(|(stat, sig)| match stat {
  2578. true => CliSignatureVerificationStatus::Pass,
  2579. false if sig == &Signature::default() => CliSignatureVerificationStatus::None,
  2580. false => CliSignatureVerificationStatus::Fail,
  2581. })
  2582. .collect()
  2583. }
  2584. }
  2585. impl fmt::Display for CliSignatureVerificationStatus {
  2586. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2587. match self {
  2588. Self::None => write!(f, "none"),
  2589. Self::Pass => write!(f, "pass"),
  2590. Self::Fail => write!(f, "fail"),
  2591. }
  2592. }
  2593. }
  2594. #[derive(Serialize, Deserialize)]
  2595. #[serde(rename_all = "camelCase")]
  2596. pub struct CliBlock {
  2597. #[serde(flatten)]
  2598. pub encoded_confirmed_block: EncodedConfirmedBlock,
  2599. #[serde(skip_serializing)]
  2600. pub slot: Slot,
  2601. }
  2602. impl QuietDisplay for CliBlock {}
  2603. impl VerboseDisplay for CliBlock {}
  2604. impl fmt::Display for CliBlock {
  2605. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2606. writeln!(f, "Slot: {}", self.slot)?;
  2607. writeln!(
  2608. f,
  2609. "Parent Slot: {}",
  2610. self.encoded_confirmed_block.parent_slot
  2611. )?;
  2612. writeln!(f, "Blockhash: {}", self.encoded_confirmed_block.blockhash)?;
  2613. writeln!(
  2614. f,
  2615. "Previous Blockhash: {}",
  2616. self.encoded_confirmed_block.previous_blockhash
  2617. )?;
  2618. if let Some(block_time) = self.encoded_confirmed_block.block_time {
  2619. writeln!(
  2620. f,
  2621. "Block Time: {:?}",
  2622. Local.timestamp_opt(block_time, 0).unwrap()
  2623. )?;
  2624. }
  2625. if let Some(block_height) = self.encoded_confirmed_block.block_height {
  2626. writeln!(f, "Block Height: {block_height:?}")?;
  2627. }
  2628. if !self.encoded_confirmed_block.rewards.is_empty() {
  2629. let mut rewards = self.encoded_confirmed_block.rewards.clone();
  2630. rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
  2631. let mut total_rewards = 0;
  2632. writeln!(f, "Rewards:")?;
  2633. writeln!(
  2634. f,
  2635. " {:<44} {:^15} {:<15} {:<20} {:>14} {:>10}",
  2636. "Address", "Type", "Amount", "New Balance", "Percent Change", "Commission"
  2637. )?;
  2638. for reward in rewards {
  2639. let sign = if reward.lamports < 0 { "-" } else { "" };
  2640. total_rewards += reward.lamports;
  2641. #[allow(clippy::format_in_format_args)]
  2642. writeln!(
  2643. f,
  2644. " {:<44} {:^15} {:>15} {} {}",
  2645. reward.pubkey,
  2646. if let Some(reward_type) = reward.reward_type {
  2647. format!("{reward_type}")
  2648. } else {
  2649. "-".to_string()
  2650. },
  2651. format!(
  2652. "{}◎{:<14.9}",
  2653. sign,
  2654. build_balance_message(reward.lamports.unsigned_abs(), false, false)
  2655. ),
  2656. if reward.post_balance == 0 {
  2657. " - -".to_string()
  2658. } else {
  2659. format!(
  2660. "◎{:<19.9} {:>13.9}%",
  2661. build_balance_message(reward.post_balance, false, false),
  2662. (reward.lamports.abs() as f64
  2663. / (reward.post_balance as f64 - reward.lamports as f64))
  2664. * 100.0
  2665. )
  2666. },
  2667. reward
  2668. .commission
  2669. .map(|commission| format!("{commission:>9}%"))
  2670. .unwrap_or_else(|| " -".to_string())
  2671. )?;
  2672. }
  2673. let sign = if total_rewards < 0 { "-" } else { "" };
  2674. writeln!(
  2675. f,
  2676. "Total Rewards: {}◎{:<12.9}",
  2677. sign,
  2678. build_balance_message(total_rewards.unsigned_abs(), false, false)
  2679. )?;
  2680. }
  2681. for (index, transaction_with_meta) in
  2682. self.encoded_confirmed_block.transactions.iter().enumerate()
  2683. {
  2684. writeln!(f, "Transaction {index}:")?;
  2685. writeln_transaction(
  2686. f,
  2687. &transaction_with_meta.transaction.decode().unwrap(),
  2688. transaction_with_meta.meta.as_ref(),
  2689. " ",
  2690. None,
  2691. None,
  2692. )?;
  2693. }
  2694. Ok(())
  2695. }
  2696. }
  2697. #[derive(Serialize, Deserialize)]
  2698. #[serde(rename_all = "camelCase")]
  2699. pub struct CliTransaction {
  2700. pub transaction: EncodedTransaction,
  2701. pub meta: Option<UiTransactionStatusMeta>,
  2702. pub block_time: Option<UnixTimestamp>,
  2703. #[serde(skip_serializing_if = "Option::is_none")]
  2704. pub slot: Option<Slot>,
  2705. #[serde(skip_serializing)]
  2706. pub decoded_transaction: VersionedTransaction,
  2707. #[serde(skip_serializing)]
  2708. pub prefix: String,
  2709. #[serde(skip_serializing_if = "Vec::is_empty")]
  2710. pub sigverify_status: Vec<CliSignatureVerificationStatus>,
  2711. }
  2712. impl QuietDisplay for CliTransaction {}
  2713. impl VerboseDisplay for CliTransaction {}
  2714. impl fmt::Display for CliTransaction {
  2715. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2716. writeln_transaction(
  2717. f,
  2718. &self.decoded_transaction,
  2719. self.meta.as_ref(),
  2720. &self.prefix,
  2721. if !self.sigverify_status.is_empty() {
  2722. Some(&self.sigverify_status)
  2723. } else {
  2724. None
  2725. },
  2726. self.block_time,
  2727. )
  2728. }
  2729. }
  2730. #[derive(Serialize, Deserialize)]
  2731. #[serde(rename_all = "camelCase")]
  2732. pub struct CliTransactionConfirmation {
  2733. pub confirmation_status: Option<TransactionConfirmationStatus>,
  2734. #[serde(flatten, skip_serializing_if = "Option::is_none")]
  2735. pub transaction: Option<CliTransaction>,
  2736. #[serde(skip_serializing)]
  2737. pub get_transaction_error: Option<String>,
  2738. #[serde(skip_serializing_if = "Option::is_none")]
  2739. pub err: Option<UiTransactionError>,
  2740. }
  2741. impl QuietDisplay for CliTransactionConfirmation {}
  2742. impl VerboseDisplay for CliTransactionConfirmation {
  2743. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  2744. if let Some(transaction) = &self.transaction {
  2745. writeln!(
  2746. w,
  2747. "\nTransaction executed in slot {}:",
  2748. transaction.slot.expect("slot should exist")
  2749. )?;
  2750. write!(w, "{transaction}")?;
  2751. } else if let Some(confirmation_status) = &self.confirmation_status {
  2752. if confirmation_status != &TransactionConfirmationStatus::Finalized {
  2753. writeln!(w)?;
  2754. writeln!(
  2755. w,
  2756. "Unable to get finalized transaction details: not yet finalized"
  2757. )?;
  2758. } else if let Some(err) = &self.get_transaction_error {
  2759. writeln!(w)?;
  2760. writeln!(w, "Unable to get finalized transaction details: {err}")?;
  2761. }
  2762. }
  2763. writeln!(w)?;
  2764. write!(w, "{self}")
  2765. }
  2766. }
  2767. impl fmt::Display for CliTransactionConfirmation {
  2768. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2769. match &self.confirmation_status {
  2770. None => write!(f, "Not found"),
  2771. Some(confirmation_status) => {
  2772. if let Some(err) = &self.err {
  2773. write!(f, "Transaction failed: {err}")
  2774. } else {
  2775. write!(f, "{confirmation_status:?}")
  2776. }
  2777. }
  2778. }
  2779. }
  2780. }
  2781. #[derive(Serialize, Deserialize)]
  2782. #[serde(rename_all = "camelCase")]
  2783. pub struct CliGossipNode {
  2784. #[serde(skip_serializing_if = "Option::is_none")]
  2785. pub ip_address: Option<String>,
  2786. #[serde(skip_serializing_if = "Option::is_none")]
  2787. pub identity_label: Option<String>,
  2788. pub identity_pubkey: String,
  2789. #[serde(skip_serializing_if = "Option::is_none")]
  2790. pub gossip_port: Option<u16>,
  2791. #[serde(skip_serializing_if = "Option::is_none")]
  2792. pub tpu_port: Option<u16>,
  2793. #[serde(skip_serializing_if = "Option::is_none")]
  2794. pub rpc_host: Option<String>,
  2795. #[serde(skip_serializing_if = "Option::is_none")]
  2796. pub pubsub_host: Option<String>,
  2797. #[serde(skip_serializing_if = "Option::is_none")]
  2798. pub version: Option<String>,
  2799. #[serde(skip_serializing_if = "Option::is_none")]
  2800. pub feature_set: Option<u32>,
  2801. #[serde(skip_serializing_if = "Option::is_none")]
  2802. pub tpu_quic_port: Option<u16>,
  2803. }
  2804. impl CliGossipNode {
  2805. pub fn new(info: RpcContactInfo, labels: &HashMap<String, String>) -> Self {
  2806. Self {
  2807. ip_address: info.gossip.map(|addr| addr.ip().to_string()),
  2808. identity_label: labels.get(&info.pubkey).cloned(),
  2809. identity_pubkey: info.pubkey,
  2810. gossip_port: info.gossip.map(|addr| addr.port()),
  2811. tpu_port: info.tpu.map(|addr| addr.port()),
  2812. rpc_host: info.rpc.map(|addr| addr.to_string()),
  2813. pubsub_host: info.pubsub.map(|addr| addr.to_string()),
  2814. version: info.version,
  2815. feature_set: info.feature_set,
  2816. tpu_quic_port: info.tpu_quic.map(|addr| addr.port()),
  2817. }
  2818. }
  2819. }
  2820. fn unwrap_to_string_or_none<T>(option: Option<T>) -> String
  2821. where
  2822. T: std::string::ToString,
  2823. {
  2824. unwrap_to_string_or_default(option, "none")
  2825. }
  2826. fn unwrap_to_string_or_default<T>(option: Option<T>, default: &str) -> String
  2827. where
  2828. T: std::string::ToString,
  2829. {
  2830. option
  2831. .as_ref()
  2832. .map(|v| v.to_string())
  2833. .unwrap_or_else(|| default.to_string())
  2834. }
  2835. impl fmt::Display for CliGossipNode {
  2836. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2837. write!(
  2838. f,
  2839. "{:15} | {:44} | {:6} | {:5} | {:8} | {:21} | {:8}| {}",
  2840. unwrap_to_string_or_none(self.ip_address.as_ref()),
  2841. self.identity_label
  2842. .as_ref()
  2843. .unwrap_or(&self.identity_pubkey),
  2844. unwrap_to_string_or_none(self.gossip_port.as_ref()),
  2845. unwrap_to_string_or_none(self.tpu_port.as_ref()),
  2846. unwrap_to_string_or_none(self.tpu_quic_port.as_ref()),
  2847. unwrap_to_string_or_none(self.rpc_host.as_ref()),
  2848. unwrap_to_string_or_default(self.version.as_ref(), "unknown"),
  2849. unwrap_to_string_or_default(self.feature_set.as_ref(), "unknown"),
  2850. )
  2851. }
  2852. }
  2853. impl QuietDisplay for CliGossipNode {}
  2854. impl VerboseDisplay for CliGossipNode {}
  2855. #[derive(Serialize, Deserialize)]
  2856. pub struct CliGossipNodes(pub Vec<CliGossipNode>);
  2857. impl fmt::Display for CliGossipNodes {
  2858. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2859. writeln!(
  2860. f,
  2861. "IP Address | Identity \
  2862. | Gossip | TPU | TPU-QUIC | RPC Address | Version | Feature Set\n\
  2863. ----------------+----------------------------------------------+\
  2864. --------+-------+----------+-----------------------+---------+----------------",
  2865. )?;
  2866. for node in self.0.iter() {
  2867. writeln!(f, "{node}")?;
  2868. }
  2869. writeln!(f, "Nodes: {}", self.0.len())
  2870. }
  2871. }
  2872. impl QuietDisplay for CliGossipNodes {}
  2873. impl VerboseDisplay for CliGossipNodes {}
  2874. #[derive(Serialize, Deserialize)]
  2875. #[serde(rename_all = "camelCase")]
  2876. pub struct CliPing {
  2877. pub source_pubkey: String,
  2878. #[serde(skip_serializing_if = "Option::is_none")]
  2879. pub fixed_blockhash: Option<String>,
  2880. #[serde(skip_serializing)]
  2881. pub blockhash_from_cluster: bool,
  2882. pub pings: Vec<CliPingData>,
  2883. pub transaction_stats: CliPingTxStats,
  2884. #[serde(skip_serializing_if = "Option::is_none")]
  2885. pub confirmation_stats: Option<CliPingConfirmationStats>,
  2886. }
  2887. impl fmt::Display for CliPing {
  2888. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2889. writeln!(f)?;
  2890. writeln_name_value(f, "Source Account:", &self.source_pubkey)?;
  2891. if let Some(fixed_blockhash) = &self.fixed_blockhash {
  2892. let blockhash_origin = if self.blockhash_from_cluster {
  2893. "fetched from cluster"
  2894. } else {
  2895. "supplied from cli arguments"
  2896. };
  2897. writeln!(
  2898. f,
  2899. "Fixed blockhash is used: {fixed_blockhash} ({blockhash_origin})"
  2900. )?;
  2901. }
  2902. writeln!(f)?;
  2903. for ping in &self.pings {
  2904. write!(f, "{ping}")?;
  2905. }
  2906. writeln!(f)?;
  2907. writeln!(f, "--- transaction statistics ---")?;
  2908. write!(f, "{}", self.transaction_stats)?;
  2909. if let Some(confirmation_stats) = &self.confirmation_stats {
  2910. write!(f, "{confirmation_stats}")?;
  2911. }
  2912. Ok(())
  2913. }
  2914. }
  2915. impl QuietDisplay for CliPing {}
  2916. impl VerboseDisplay for CliPing {}
  2917. #[derive(Serialize, Deserialize)]
  2918. #[serde(rename_all = "camelCase")]
  2919. pub struct CliPingData {
  2920. pub success: bool,
  2921. #[serde(skip_serializing_if = "Option::is_none")]
  2922. pub signature: Option<String>,
  2923. #[serde(skip_serializing_if = "Option::is_none")]
  2924. pub ms: Option<u64>,
  2925. #[serde(skip_serializing_if = "Option::is_none")]
  2926. pub error: Option<String>,
  2927. #[serde(skip_serializing)]
  2928. pub print_timestamp: bool,
  2929. pub timestamp: String,
  2930. pub sequence: u64,
  2931. #[serde(skip_serializing_if = "Option::is_none")]
  2932. pub lamports: Option<u64>,
  2933. }
  2934. impl fmt::Display for CliPingData {
  2935. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2936. let (mark, msg) = if let Some(signature) = &self.signature {
  2937. if self.success {
  2938. (
  2939. CHECK_MARK,
  2940. format!(
  2941. "{} lamport(s) transferred: seq={:<3} time={:>4}ms signature={}",
  2942. self.lamports.unwrap(),
  2943. self.sequence,
  2944. self.ms.unwrap(),
  2945. signature
  2946. ),
  2947. )
  2948. } else if let Some(error) = &self.error {
  2949. (
  2950. CROSS_MARK,
  2951. format!(
  2952. "Transaction failed: seq={:<3} error={:?} signature={}",
  2953. self.sequence, error, signature
  2954. ),
  2955. )
  2956. } else {
  2957. (
  2958. CROSS_MARK,
  2959. format!(
  2960. "Confirmation timeout: seq={:<3} signature={}",
  2961. self.sequence, signature
  2962. ),
  2963. )
  2964. }
  2965. } else {
  2966. (
  2967. CROSS_MARK,
  2968. format!(
  2969. "Submit failed: seq={:<3} error={:?}",
  2970. self.sequence,
  2971. self.error.as_ref().unwrap(),
  2972. ),
  2973. )
  2974. };
  2975. writeln!(
  2976. f,
  2977. "{}{}{}",
  2978. if self.print_timestamp {
  2979. &self.timestamp
  2980. } else {
  2981. ""
  2982. },
  2983. mark,
  2984. msg
  2985. )
  2986. }
  2987. }
  2988. impl QuietDisplay for CliPingData {}
  2989. impl VerboseDisplay for CliPingData {}
  2990. #[derive(Serialize, Deserialize)]
  2991. #[serde(rename_all = "camelCase")]
  2992. pub struct CliPingTxStats {
  2993. pub num_transactions: u32,
  2994. pub num_transaction_confirmed: u32,
  2995. }
  2996. impl fmt::Display for CliPingTxStats {
  2997. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  2998. writeln!(
  2999. f,
  3000. "{} transactions submitted, {} transactions confirmed, {:.1}% transaction loss",
  3001. self.num_transactions,
  3002. self.num_transaction_confirmed,
  3003. (100.
  3004. - f64::from(self.num_transaction_confirmed) / f64::from(self.num_transactions)
  3005. * 100.)
  3006. )
  3007. }
  3008. }
  3009. impl QuietDisplay for CliPingTxStats {}
  3010. impl VerboseDisplay for CliPingTxStats {}
  3011. #[derive(Serialize, Deserialize)]
  3012. #[serde(rename_all = "camelCase")]
  3013. pub struct CliPingConfirmationStats {
  3014. pub min: f64,
  3015. pub mean: f64,
  3016. pub max: f64,
  3017. pub std_dev: f64,
  3018. }
  3019. impl fmt::Display for CliPingConfirmationStats {
  3020. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  3021. writeln!(
  3022. f,
  3023. "confirmation min/mean/max/stddev = {:.0}/{:.0}/{:.0}/{:.0} ms",
  3024. self.min, self.mean, self.max, self.std_dev,
  3025. )
  3026. }
  3027. }
  3028. impl QuietDisplay for CliPingConfirmationStats {}
  3029. impl VerboseDisplay for CliPingConfirmationStats {}
  3030. #[derive(Serialize, Deserialize, Debug)]
  3031. #[serde(rename_all = "camelCase")]
  3032. pub struct CliBalance {
  3033. pub lamports: u64,
  3034. #[serde(skip)]
  3035. pub config: BuildBalanceMessageConfig,
  3036. }
  3037. impl QuietDisplay for CliBalance {
  3038. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  3039. let config = BuildBalanceMessageConfig {
  3040. show_unit: false,
  3041. trim_trailing_zeros: true,
  3042. ..self.config
  3043. };
  3044. let balance_message = build_balance_message_with_config(self.lamports, &config);
  3045. write!(w, "{balance_message}")
  3046. }
  3047. }
  3048. impl VerboseDisplay for CliBalance {
  3049. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  3050. let config = BuildBalanceMessageConfig {
  3051. show_unit: true,
  3052. trim_trailing_zeros: false,
  3053. ..self.config
  3054. };
  3055. let balance_message = build_balance_message_with_config(self.lamports, &config);
  3056. write!(w, "{balance_message}")
  3057. }
  3058. }
  3059. impl fmt::Display for CliBalance {
  3060. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  3061. let balance_message = build_balance_message_with_config(self.lamports, &self.config);
  3062. write!(f, "{balance_message}")
  3063. }
  3064. }
  3065. #[derive(Serialize, Deserialize)]
  3066. #[serde(rename_all = "camelCase")]
  3067. pub struct CliFindProgramDerivedAddress {
  3068. pub address: String,
  3069. pub bump_seed: u8,
  3070. }
  3071. impl QuietDisplay for CliFindProgramDerivedAddress {}
  3072. impl VerboseDisplay for CliFindProgramDerivedAddress {}
  3073. impl fmt::Display for CliFindProgramDerivedAddress {
  3074. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  3075. write!(f, "{}", self.address)?;
  3076. Ok(())
  3077. }
  3078. }
  3079. #[cfg(test)]
  3080. mod tests {
  3081. use {
  3082. super::*,
  3083. clap::{App, Arg},
  3084. solana_keypair::keypair_from_seed,
  3085. solana_message::Message,
  3086. solana_pubkey::Pubkey,
  3087. solana_signature::Signature,
  3088. solana_signer::{null_signer::NullSigner, Signer, SignerError},
  3089. solana_system_interface::instruction::transfer,
  3090. solana_transaction::Transaction,
  3091. };
  3092. #[test]
  3093. fn test_return_signers() {
  3094. struct BadSigner {
  3095. pubkey: Pubkey,
  3096. }
  3097. impl BadSigner {
  3098. pub fn new(pubkey: Pubkey) -> Self {
  3099. Self { pubkey }
  3100. }
  3101. }
  3102. impl Signer for BadSigner {
  3103. fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
  3104. Ok(self.pubkey)
  3105. }
  3106. fn try_sign_message(&self, _message: &[u8]) -> Result<Signature, SignerError> {
  3107. Ok(Signature::from([1u8; 64]))
  3108. }
  3109. fn is_interactive(&self) -> bool {
  3110. false
  3111. }
  3112. }
  3113. let present: Box<dyn Signer> = Box::new(keypair_from_seed(&[2u8; 32]).unwrap());
  3114. let absent: Box<dyn Signer> = Box::new(NullSigner::new(&Pubkey::from([3u8; 32])));
  3115. let bad: Box<dyn Signer> = Box::new(BadSigner::new(Pubkey::from([4u8; 32])));
  3116. let to = Pubkey::from([5u8; 32]);
  3117. let nonce = Pubkey::from([6u8; 32]);
  3118. let from = present.pubkey();
  3119. let fee_payer = absent.pubkey();
  3120. let nonce_auth = bad.pubkey();
  3121. let mut tx = Transaction::new_unsigned(Message::new_with_nonce(
  3122. vec![transfer(&from, &to, 42)],
  3123. Some(&fee_payer),
  3124. &nonce,
  3125. &nonce_auth,
  3126. ));
  3127. let signers = vec![present.as_ref(), absent.as_ref(), bad.as_ref()];
  3128. let blockhash = Hash::new_from_array([7u8; 32]);
  3129. tx.try_partial_sign(&signers, blockhash).unwrap();
  3130. let res = return_signers(&tx, &OutputFormat::JsonCompact).unwrap();
  3131. let sign_only = parse_sign_only_reply_string(&res);
  3132. assert_eq!(sign_only.blockhash, blockhash);
  3133. assert_eq!(sign_only.message, None);
  3134. assert_eq!(sign_only.present_signers[0].0, present.pubkey());
  3135. assert_eq!(sign_only.absent_signers[0], absent.pubkey());
  3136. assert_eq!(sign_only.bad_signers[0], bad.pubkey());
  3137. let res_data = return_signers_data(&tx, &ReturnSignersConfig::default());
  3138. assert_eq!(
  3139. res_data,
  3140. CliSignOnlyData {
  3141. blockhash: blockhash.to_string(),
  3142. message: None,
  3143. signers: vec![format!("{}={}", present.pubkey(), tx.signatures[1])],
  3144. absent: vec![absent.pubkey().to_string()],
  3145. bad_sig: vec![bad.pubkey().to_string()],
  3146. }
  3147. );
  3148. let expected_msg = "AwECBwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgTl3Dqh9\
  3149. F19Wo1Rmw0x+zMuNipG07jeiXfYPW4/Js5QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE\
  3150. BAQEBAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBgYGBgYGBgYGBgYGBgYGBgYG\
  3151. BgYGBgYGBgYGBgYGBgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAan1RcZLFaO\
  3152. 4IqEX3PSl4jPA1wxRbIas0TYBi6pQAAABwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcH\
  3153. BwcCBQMEBgIEBAAAAAUCAQMMAgAAACoAAAAAAAAA"
  3154. .to_string();
  3155. let config = ReturnSignersConfig {
  3156. dump_transaction_message: true,
  3157. };
  3158. let res = return_signers_with_config(&tx, &OutputFormat::JsonCompact, &config).unwrap();
  3159. let sign_only = parse_sign_only_reply_string(&res);
  3160. assert_eq!(sign_only.blockhash, blockhash);
  3161. assert_eq!(sign_only.message, Some(expected_msg.clone()));
  3162. assert_eq!(sign_only.present_signers[0].0, present.pubkey());
  3163. assert_eq!(sign_only.absent_signers[0], absent.pubkey());
  3164. assert_eq!(sign_only.bad_signers[0], bad.pubkey());
  3165. let res_data = return_signers_data(&tx, &config);
  3166. assert_eq!(
  3167. res_data,
  3168. CliSignOnlyData {
  3169. blockhash: blockhash.to_string(),
  3170. message: Some(expected_msg),
  3171. signers: vec![format!("{}={}", present.pubkey(), tx.signatures[1])],
  3172. absent: vec![absent.pubkey().to_string()],
  3173. bad_sig: vec![bad.pubkey().to_string()],
  3174. }
  3175. );
  3176. }
  3177. #[test]
  3178. fn test_verbose_quiet_output_formats() {
  3179. #[derive(Deserialize, Serialize)]
  3180. struct FallbackToDisplay {}
  3181. impl std::fmt::Display for FallbackToDisplay {
  3182. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  3183. write!(f, "display")
  3184. }
  3185. }
  3186. impl QuietDisplay for FallbackToDisplay {}
  3187. impl VerboseDisplay for FallbackToDisplay {}
  3188. let f = FallbackToDisplay {};
  3189. assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
  3190. assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "display");
  3191. assert_eq!(
  3192. &OutputFormat::DisplayVerbose.formatted_string(&f),
  3193. "display"
  3194. );
  3195. #[derive(Deserialize, Serialize)]
  3196. struct DiscreteVerbosityDisplay {}
  3197. impl std::fmt::Display for DiscreteVerbosityDisplay {
  3198. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  3199. write!(f, "display")
  3200. }
  3201. }
  3202. impl QuietDisplay for DiscreteVerbosityDisplay {
  3203. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  3204. write!(w, "quiet")
  3205. }
  3206. }
  3207. impl VerboseDisplay for DiscreteVerbosityDisplay {
  3208. fn write_str(&self, w: &mut dyn std::fmt::Write) -> std::fmt::Result {
  3209. write!(w, "verbose")
  3210. }
  3211. }
  3212. let f = DiscreteVerbosityDisplay {};
  3213. assert_eq!(&OutputFormat::Display.formatted_string(&f), "display");
  3214. assert_eq!(&OutputFormat::DisplayQuiet.formatted_string(&f), "quiet");
  3215. assert_eq!(
  3216. &OutputFormat::DisplayVerbose.formatted_string(&f),
  3217. "verbose"
  3218. );
  3219. }
  3220. #[test]
  3221. fn test_output_format_from_matches() {
  3222. let app = App::new("test").arg(
  3223. Arg::with_name("output_format")
  3224. .long("output")
  3225. .value_name("FORMAT")
  3226. .global(true)
  3227. .takes_value(true)
  3228. .possible_values(&["json", "json-compact"])
  3229. .help("Return information in specified output format"),
  3230. );
  3231. let matches = app
  3232. .clone()
  3233. .get_matches_from(vec!["test", "--output", "json"]);
  3234. assert_eq!(
  3235. OutputFormat::from_matches(&matches, "output_format", false),
  3236. OutputFormat::Json
  3237. );
  3238. assert_eq!(
  3239. OutputFormat::from_matches(&matches, "output_format", true),
  3240. OutputFormat::Json
  3241. );
  3242. let matches = app
  3243. .clone()
  3244. .get_matches_from(vec!["test", "--output", "json-compact"]);
  3245. assert_eq!(
  3246. OutputFormat::from_matches(&matches, "output_format", false),
  3247. OutputFormat::JsonCompact
  3248. );
  3249. assert_eq!(
  3250. OutputFormat::from_matches(&matches, "output_format", true),
  3251. OutputFormat::JsonCompact
  3252. );
  3253. let matches = app.clone().get_matches_from(vec!["test"]);
  3254. assert_eq!(
  3255. OutputFormat::from_matches(&matches, "output_format", false),
  3256. OutputFormat::Display
  3257. );
  3258. assert_eq!(
  3259. OutputFormat::from_matches(&matches, "output_format", true),
  3260. OutputFormat::DisplayVerbose
  3261. );
  3262. }
  3263. #[test]
  3264. fn test_format_vote_account() {
  3265. let epoch_rewards = vec![
  3266. CliEpochReward {
  3267. percent_change: 11.0,
  3268. post_balance: 100,
  3269. commission: Some(1),
  3270. effective_slot: 100,
  3271. epoch: 1,
  3272. amount: 10,
  3273. block_time: 0,
  3274. apr: Some(10.0),
  3275. },
  3276. CliEpochReward {
  3277. percent_change: 11.0,
  3278. post_balance: 100,
  3279. commission: Some(1),
  3280. effective_slot: 200,
  3281. epoch: 2,
  3282. amount: 12,
  3283. block_time: 1_000_000,
  3284. apr: Some(13.0),
  3285. },
  3286. ];
  3287. let mut c = CliVoteAccount {
  3288. account_balance: 10000,
  3289. validator_identity: Pubkey::default().to_string(),
  3290. epoch_rewards: Some(epoch_rewards),
  3291. recent_timestamp: BlockTimestamp::default(),
  3292. ..CliVoteAccount::default()
  3293. };
  3294. let s = format!("{c}");
  3295. assert_eq!(s, "Account Balance: 0.00001 SOL\nValidator Identity: 11111111111111111111111111111111\nVote Authority: None\nWithdraw Authority: \nCredits: 0\nCommission: 0%\nRoot Slot: ~\nRecent Timestamp: 1970-01-01T00:00:00Z from slot 0\nEpoch Rewards:\n Epoch Reward Slot Time Amount New Balance Percent Change APR Commission\n 1 100 1970-01-01 00:00:00 UTC ◎0.00000001 ◎0.0000001 11.000% 10.00% 1%\n 2 200 1970-01-12 13:46:40 UTC ◎0.000000012 ◎0.0000001 11.000% 13.00% 1%\n");
  3296. println!("{s}");
  3297. c.use_csv = true;
  3298. let s = format!("{c}");
  3299. assert_eq!(s, "Account Balance: 0.00001 SOL\nValidator Identity: 11111111111111111111111111111111\nVote Authority: None\nWithdraw Authority: \nCredits: 0\nCommission: 0%\nRoot Slot: ~\nRecent Timestamp: 1970-01-01T00:00:00Z from slot 0\nEpoch Rewards:\nEpoch,Reward Slot,Time,Amount,New Balance,Percent Change,APR,Commission\n1,100,1970-01-01 00:00:00 UTC,0.00000001,0.0000001,11%,10.00%,1%\n2,200,1970-01-12 13:46:40 UTC,0.000000012,0.0000001,11%,13.00%,1%\n");
  3300. println!("{s}");
  3301. }
  3302. }