types.rs (16792B)
1 use secrecy::{ExposeSecret, SecretString}; 2 use serde::{Deserialize, Serialize, Serializer}; 3 4 use crate::{ 5 adapters::{config_store::UiConfig, ui_data_store::BlockMetricRow}, 6 app::PeerInfo, 7 domain::{Amount, BurnLeaderRank, OutPoint, Transaction, TransactionV2, TxOutput}, 8 ip_geolocation::CountryCode, 9 }; 10 11 #[derive(Debug, Deserialize)] 12 pub(super) struct AuthForm { 13 pub(super) password: SecretString, 14 } 15 16 #[derive(Debug, Deserialize)] 17 pub(super) struct ChangePasswordForm { 18 pub(super) old_password: SecretString, 19 pub(super) new_password: SecretString, 20 } 21 22 #[derive(Debug, Serialize)] 23 pub(super) struct AuthStatusResponse { 24 pub(super) configured: bool, 25 pub(super) authenticated: bool, 26 } 27 28 #[derive(Debug, Serialize)] 29 pub(super) struct NetworkHealthResponse { 30 pub(super) ok: bool, 31 pub(super) state: String, 32 pub(super) local_height: u64, 33 pub(super) local_tip_hash: String, 34 pub(super) finalized_height: Option<u64>, 35 pub(super) finalized_hash: Option<String>, 36 pub(super) last_block_age_ms: Option<u64>, 37 pub(super) best_known_height: u64, 38 pub(super) sync_start_height: Option<u64>, 39 pub(super) sync_validated_height: Option<u64>, 40 pub(super) sync_target_height: Option<u64>, 41 pub(super) shared_height: u64, 42 pub(super) lag_blocks: u64, 43 pub(super) outbound_peers: usize, 44 pub(super) inbound_peers: usize, 45 pub(super) healthy_peers: usize, 46 pub(super) failed_peers: usize, 47 pub(super) stale_peers: usize, 48 pub(super) banned_peers: usize, 49 pub(super) pending_transactions: usize, 50 pub(super) pending_plain_transactions: usize, 51 pub(super) pending_v2_transactions: usize, 52 pub(super) last_finalizer_mode: Option<String>, 53 pub(super) last_finalizer_rank: Option<u32>, 54 pub(super) last_block_finalizer: Option<String>, 55 pub(super) current_leader: Option<String>, 56 pub(super) wallet_is_current_leader: bool, 57 pub(super) last_auto_finalization_status: Option<String>, 58 pub(super) vdf_rounds: u64, 59 pub(super) vdf_target_block_ms: u64, 60 pub(super) rejected_blocks: u64, 61 pub(super) rejected_block_batches: u64, 62 pub(super) rejected_snapshots: u64, 63 pub(super) rejected_chain_payloads: u64, 64 pub(super) last_chain_payload_error: Option<String>, 65 pub(super) network_time_offset_ms: Option<i64>, 66 pub(super) bad_clock_peers: usize, 67 pub(super) last_error: Option<String>, 68 } 69 70 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 71 pub(super) struct PeerPresentation { 72 #[serde(flatten)] 73 pub(super) peer: PeerInfo, 74 #[serde(skip_serializing_if = "Option::is_none")] 75 pub(super) country_code: Option<CountryCode>, 76 } 77 78 #[derive(Clone, Copy, Debug, Default)] 79 pub(super) struct MempoolCounts { 80 pub(super) plain_transactions: usize, 81 pub(super) v2_transactions: usize, 82 } 83 84 impl MempoolCounts { 85 pub(super) fn total(&self) -> usize { 86 self.plain_transactions.saturating_add(self.v2_transactions) 87 } 88 } 89 90 #[derive(Clone, Debug)] 91 pub(super) struct NetworkHealthLocalState { 92 pub(super) height: u64, 93 pub(super) tip_hash: String, 94 pub(super) finalized_height: Option<u64>, 95 pub(super) finalized_hash: Option<String>, 96 pub(super) tip_timestamp_ms: Option<u64>, 97 pub(super) sync_start_height: Option<u64>, 98 pub(super) sync_validated_height: Option<u64>, 99 pub(super) sync_target_height: Option<u64>, 100 pub(super) pending_transactions: usize, 101 pub(super) last_finalizer_mode: Option<String>, 102 pub(super) last_finalizer_rank: Option<u32>, 103 pub(super) last_block_finalizer: Option<String>, 104 pub(super) current_leader: Option<String>, 105 pub(super) wallet_is_current_leader: bool, 106 pub(super) last_auto_finalization_status: Option<String>, 107 pub(super) vdf_rounds: u64, 108 pub(super) vdf_target_block_ms: u64, 109 pub(super) rejected_blocks: u64, 110 pub(super) rejected_block_batches: u64, 111 pub(super) rejected_snapshots: u64, 112 pub(super) last_chain_payload_error: Option<String>, 113 } 114 115 #[derive(Debug, Deserialize)] 116 pub(super) struct BurnSettingsForm { 117 pub(super) enabled: Option<bool>, 118 pub(super) amount: Amount, 119 pub(super) fee_per_byte: Option<Amount>, 120 } 121 122 #[derive(Debug, Deserialize)] 123 pub(super) struct RecoveryVdfSettingsForm { 124 pub(super) top_rank_percent: u8, 125 } 126 127 #[derive(Debug, Deserialize)] 128 pub(super) struct PowMiningForm { 129 pub(super) enabled: bool, 130 pub(super) workers: Option<u8>, 131 } 132 133 #[derive(Debug, Deserialize)] 134 pub(super) struct MetricsSettingsForm { 135 pub(super) enabled: bool, 136 } 137 138 #[derive(Debug, Deserialize)] 139 pub(super) struct ChainResetForm { 140 pub(super) confirm: String, 141 } 142 143 #[derive(Debug, Deserialize)] 144 pub(super) struct P2pAnnounceForm { 145 pub(super) addr: String, 146 } 147 148 #[derive(Debug, Deserialize)] 149 pub(super) struct P2pInboundForm { 150 pub(super) enabled: bool, 151 pub(super) bind_port: Option<u16>, 152 } 153 154 #[derive(Debug, Deserialize)] 155 pub(super) struct StratumSettingsForm { 156 pub(super) enabled: bool, 157 pub(super) bind_port: Option<u16>, 158 } 159 160 #[derive(Debug, Deserialize)] 161 pub(super) struct WalletEndpointSettingsForm { 162 pub(super) enabled: bool, 163 pub(super) bind_port: Option<u16>, 164 } 165 166 #[derive(Debug, Serialize)] 167 pub(super) struct ConfigResponse { 168 #[serde(flatten)] 169 pub(super) config: UiConfig, 170 pub(super) p2p_inbound_runtime_active: bool, 171 pub(super) p2p_runtime_bind_addr: String, 172 pub(super) stratum_runtime_enabled: bool, 173 pub(super) stratum_runtime_listen_addr: Option<String>, 174 pub(super) wallet_endpoint_runtime_enabled: bool, 175 pub(super) wallet_endpoint_runtime_listen_addr: Option<String>, 176 } 177 178 #[derive(Debug, Deserialize)] 179 pub(super) struct TransferForm { 180 pub(super) to: String, 181 pub(super) amount: Amount, 182 pub(super) fee_per_byte: Option<Amount>, 183 #[serde(default)] 184 pub(super) utxos: String, 185 } 186 187 #[derive(Debug, Deserialize)] 188 pub(super) struct PeerForm { 189 pub(super) peer: String, 190 } 191 192 #[derive(Debug, Deserialize)] 193 pub(super) struct AddressBookForm { 194 pub(super) address: String, 195 pub(super) name: String, 196 pub(super) old_address: Option<String>, 197 } 198 199 #[derive(Debug, Deserialize)] 200 pub(super) struct AddressBookDeleteForm { 201 pub(super) address: String, 202 } 203 204 #[derive(Debug, Deserialize)] 205 pub(super) struct ConfigForm { 206 pub(super) setup_complete: bool, 207 #[serde(default)] 208 pub(super) peer: String, 209 } 210 211 #[derive(Debug, Deserialize)] 212 pub(super) struct SeedPhraseForm { 213 pub(super) seed_phrase: SecretString, 214 } 215 216 #[derive(Debug, Deserialize)] 217 pub(super) struct BlocksQuery { 218 pub(super) before_height: Option<u64>, 219 pub(super) limit: Option<usize>, 220 } 221 222 #[derive(Debug, Deserialize)] 223 pub(super) struct MetricsQuery { 224 pub(super) limit: Option<usize>, 225 } 226 227 #[derive(Debug, Default, Deserialize)] 228 pub(super) struct PageQuery { 229 pub(super) offset: Option<usize>, 230 pub(super) limit: Option<usize>, 231 } 232 233 #[derive(Debug, Default, Deserialize)] 234 pub(super) struct WalletTransactionsQuery { 235 pub(super) tx: Option<bool>, 236 pub(super) mine: Option<bool>, 237 pub(super) burn: Option<bool>, 238 pub(super) reward: Option<bool>, 239 pub(super) offset: Option<usize>, 240 pub(super) limit: Option<usize>, 241 } 242 243 impl WalletTransactionsQuery { 244 pub(super) fn page(&self) -> PageQuery { 245 PageQuery { 246 offset: self.offset, 247 limit: self.limit, 248 } 249 } 250 } 251 252 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 253 pub(crate) struct WalletTransactionFilters { 254 pub(crate) transfer: bool, 255 pub(crate) mine: bool, 256 pub(crate) burn: bool, 257 pub(crate) reward: bool, 258 } 259 260 impl Default for WalletTransactionFilters { 261 fn default() -> Self { 262 Self { 263 transfer: true, 264 mine: true, 265 burn: true, 266 reward: true, 267 } 268 } 269 } 270 271 impl WalletTransactionFilters { 272 pub(super) fn from_query(query: WalletTransactionsQuery) -> Self { 273 Self { 274 transfer: query.tx.unwrap_or(true), 275 mine: query.mine.unwrap_or(true), 276 burn: query.burn.unwrap_or(true), 277 reward: query.reward.unwrap_or(true), 278 } 279 } 280 281 pub(super) fn allows(self, transaction: &Transaction) -> bool { 282 match transaction { 283 Transaction::Transfer { .. } => self.transfer, 284 Transaction::Mine { .. } => self.mine, 285 Transaction::Burn { .. } => self.burn, 286 } 287 } 288 289 pub(super) fn allows_v2(self, transaction: &TransactionV2) -> bool { 290 match transaction { 291 TransactionV2::Migration { .. } | TransactionV2::Transfer { .. } => self.transfer, 292 TransactionV2::Mine { .. } => self.mine, 293 TransactionV2::Burn { .. } => self.burn, 294 } 295 } 296 } 297 298 #[derive(Debug, Serialize)] 299 pub(super) struct ActionResponse { 300 pub(super) ok: bool, 301 pub(super) error: Option<String>, 302 } 303 304 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 305 #[serde(rename_all = "camelCase")] 306 pub(super) struct Page<T> { 307 pub(super) items: Vec<T>, 308 pub(super) offset: usize, 309 pub(super) limit: usize, 310 pub(super) total: usize, 311 pub(super) has_more: bool, 312 pub(super) next_offset: Option<usize>, 313 } 314 315 #[derive(Clone, Debug, PartialEq, Serialize)] 316 #[serde(rename_all = "camelCase")] 317 pub(super) struct MetricsResponse { 318 pub(super) enabled: bool, 319 pub(super) preparing: bool, 320 pub(super) latest: Option<BlockMetricRow>, 321 pub(super) charts: Vec<MetricsChart>, 322 pub(super) leaderboards: MetricsLeaderboards, 323 pub(super) top_mine_proofs: Vec<MineProofLeaderboardEntry>, 324 } 325 326 #[derive(Clone, Debug, Default, PartialEq, Serialize)] 327 #[serde(rename_all = "camelCase")] 328 pub(super) struct MetricsLeaderboards { 329 pub(super) balances: Vec<LeaderboardEntry>, 330 pub(super) miners: Vec<LeaderboardEntry>, 331 pub(super) burners: Vec<LeaderboardEntry>, 332 } 333 334 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 335 #[serde(rename_all = "camelCase")] 336 pub(super) struct LeaderboardEntry { 337 pub(super) address: String, 338 pub(super) amount: Amount, 339 pub(super) count: u64, 340 } 341 342 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 343 #[serde(rename_all = "camelCase")] 344 pub(super) struct MineProofLeaderboardEntry { 345 pub(super) height: u64, 346 pub(super) address: String, 347 pub(super) proof_bits: u32, 348 pub(super) difficulty_bits: u32, 349 pub(super) proof_hash: String, 350 } 351 352 #[derive(Clone, Debug, PartialEq, Serialize)] 353 #[serde(rename_all = "camelCase")] 354 pub(super) struct MetricsChart { 355 pub(super) id: &'static str, 356 pub(super) title: &'static str, 357 pub(super) unit: &'static str, 358 pub(super) value_kind: MetricsValueKind, 359 pub(super) points: Vec<MetricsPoint>, 360 } 361 362 #[derive(Clone, Debug, PartialEq, Serialize)] 363 #[serde(rename_all = "camelCase")] 364 pub(super) struct MetricsPoint { 365 pub(super) height: u64, 366 pub(super) value: f64, 367 } 368 369 #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] 370 #[serde(rename_all = "camelCase")] 371 pub(super) enum MetricsValueKind { 372 Number, 373 Seconds, 374 Iuna, 375 Bytes, 376 } 377 378 #[derive(Debug, Serialize)] 379 #[serde(rename_all = "camelCase")] 380 pub(super) struct FeeEstimateResponse { 381 pub(super) ok: bool, 382 pub(super) error: Option<String>, 383 pub(super) bytes: Option<usize>, 384 pub(super) fee: Option<Amount>, 385 } 386 387 #[derive(Debug, Serialize)] 388 pub(super) struct WalletSetupResponse { 389 pub(super) ok: bool, 390 pub(super) error: Option<String>, 391 pub(super) address: Option<String>, 392 #[serde(serialize_with = "serialize_optional_secret")] 393 pub(super) seed_phrase: Option<SecretString>, 394 pub(super) dev_verify_bypass: bool, 395 pub(super) requires_peer: bool, 396 } 397 398 fn serialize_optional_secret<S>( 399 value: &Option<SecretString>, 400 serializer: S, 401 ) -> Result<S::Ok, S::Error> 402 where 403 S: Serializer, 404 { 405 match value { 406 Some(secret) => serializer.serialize_some(secret.expose_secret()), 407 None => serializer.serialize_none(), 408 } 409 } 410 411 #[cfg(test)] 412 mod secret_tests { 413 use super::{AuthForm, ChangePasswordForm}; 414 415 #[test] 416 fn auth_form_debug_output_redacts_passwords() { 417 let auth = AuthForm { 418 password: "correct-horse-battery-staple".into(), 419 }; 420 let change = ChangePasswordForm { 421 old_password: "old-password-value".into(), 422 new_password: "new-password-value".into(), 423 }; 424 425 let debug = format!("{auth:?} {change:?}"); 426 assert!(debug.contains("[REDACTED]")); 427 assert!(!debug.contains("correct-horse-battery-staple")); 428 assert!(!debug.contains("old-password-value")); 429 assert!(!debug.contains("new-password-value")); 430 } 431 } 432 433 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 434 #[serde(rename_all = "camelCase")] 435 pub(crate) struct WalletTransactionRow { 436 pub(super) kind: &'static str, 437 pub(super) from: String, 438 pub(super) to: Option<String>, 439 pub(super) amount: Amount, 440 pub(super) fee: Amount, 441 pub(super) inputs: Vec<UiTxInput>, 442 pub(super) outputs: Vec<TxOutput>, 443 pub(super) change: Vec<TxOutput>, 444 pub(super) signature: String, 445 pub(super) status: &'static str, 446 pub(super) block_height: Option<u64>, 447 pub(super) timestamp_ms: Option<u64>, 448 pub(super) block_finalizer: Option<String>, 449 pub(super) direction: &'static str, 450 pub(super) difficulty_bits: Option<u32>, 451 pub(super) proof_bits: Option<u32>, 452 pub(super) proof_hash: Option<String>, 453 pub(super) reward_total: Option<Amount>, 454 pub(super) reward_fee_inputs: Vec<UiRewardFeeInput>, 455 pub(super) reward_outputs: Vec<UiRewardOutput>, 456 } 457 458 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 459 #[serde(rename_all = "camelCase")] 460 pub(super) struct UiRewardFeeInput { 461 pub(super) transaction_kind: &'static str, 462 pub(super) amount: Amount, 463 pub(super) owner: String, 464 pub(super) signature: String, 465 } 466 467 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 468 #[serde(rename_all = "camelCase")] 469 pub(super) struct UiRewardOutput { 470 pub(super) label: String, 471 pub(super) amount: Amount, 472 pub(super) address: String, 473 } 474 475 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 476 #[serde(rename_all = "camelCase")] 477 pub(super) struct WalletUtxoRow { 478 pub(super) outpoint: OutPoint, 479 pub(super) address: String, 480 pub(super) amount: Amount, 481 pub(super) spendable: bool, 482 } 483 484 #[derive(Clone, Debug)] 485 pub(crate) struct WalletTransactionContext { 486 pub(crate) status: &'static str, 487 pub(crate) block_height: Option<u64>, 488 pub(crate) timestamp_ms: Option<u64>, 489 pub(crate) block_finalizer: Option<String>, 490 } 491 492 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 493 pub(super) struct UiBlock { 494 pub(super) height: u64, 495 pub(super) prev_hash: String, 496 pub(super) timestamp_ms: u64, 497 pub(super) miner: String, 498 pub(super) finalizer_mode: crate::domain::FinalizerMode, 499 pub(super) finalizer_rank: u32, 500 pub(super) reward: Amount, 501 pub(super) total_fees: Amount, 502 pub(super) lost_iuna: Amount, 503 pub(super) total_bytes: usize, 504 pub(super) header_and_proof_bytes: usize, 505 pub(super) transaction_bytes: usize, 506 pub(super) transaction_byte_breakdown: Vec<UiByteBreakdown>, 507 pub(super) burn_bundle_bytes: usize, 508 pub(super) burn_bundle_quorum: UiBurnBundleQuorum, 509 pub(super) vdf_rounds: u64, 510 pub(super) vdf_output: String, 511 pub(super) leader_proof: Option<crate::domain::LeaderProof>, 512 pub(super) burn_leader_ranks: Vec<BurnLeaderRank>, 513 pub(super) transactions: Vec<UiTransaction>, 514 pub(super) burn_bundles: Vec<UiBurnBundle>, 515 pub(super) hash: String, 516 } 517 518 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 519 pub(super) struct UiBurnBundleQuorum { 520 pub(super) burn_bundles_included: usize, 521 pub(super) committee_size: usize, 522 } 523 524 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 525 pub(super) struct UiByteBreakdown { 526 pub(super) label: &'static str, 527 pub(super) bytes: usize, 528 } 529 530 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 531 #[serde(rename_all = "camelCase")] 532 pub(super) struct UiBurnBundle { 533 pub(super) slot: u8, 534 pub(super) member: String, 535 pub(super) hash: String, 536 pub(super) byte_size: usize, 537 pub(super) burns: Vec<UiTransaction>, 538 } 539 540 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 541 pub(super) struct UiTransaction { 542 pub(super) kind: &'static str, 543 pub(super) from: String, 544 pub(super) to: Option<String>, 545 pub(super) amount: Amount, 546 pub(super) fee: Amount, 547 pub(super) inputs: Vec<UiTxInput>, 548 pub(super) outputs: Vec<TxOutput>, 549 pub(super) change: Vec<TxOutput>, 550 pub(super) signature: String, 551 pub(super) difficulty_bits: Option<u32>, 552 pub(super) proof_bits: Option<u32>, 553 pub(super) proof_hash: Option<String>, 554 } 555 556 #[derive(Clone, Debug, Eq, PartialEq, Serialize)] 557 pub(super) struct UiTxInput { 558 pub(super) outpoint: OutPoint, 559 pub(super) owner: String, 560 pub(super) signature: String, 561 pub(super) amount: Option<Amount>, 562 pub(super) address: Option<String>, 563 }