From 16f679995b7e5ba34ecdfbdcb71c556ca33c50af Mon Sep 17 00:00:00 2001 From: dorianvp Date: Wed, 10 Dec 2025 14:14:45 -0300 Subject: [PATCH 1/2] chore: temporary fix for regtest --- zebra-crosslink/wallet/src/lib.rs | 765 +++++-- zebra-crosslink/zebra-crosslink/src/lib.rs | 140 +- zebra-gui/src/ui.rs | 2311 +++++++++++++++----- 3 files changed, 2468 insertions(+), 748 deletions(-) diff --git a/zebra-crosslink/wallet/src/lib.rs b/zebra-crosslink/wallet/src/lib.rs index 2c730711..b7405262 100644 --- a/zebra-crosslink/wallet/src/lib.rs +++ b/zebra-crosslink/wallet/src/lib.rs @@ -1,13 +1,12 @@ //! Internal wallet #![allow(warnings)] -use zcash_client_backend::data_api::WalletCommitmentTrees; use orchard::keys::SpendAuthorizingKey; use orchard::note_encryption::CompactAction; use rand_chacha::rand_core::SeedableRng; use rand_core::OsRng; use sapling_crypto::zip32::ExtendedSpendingKey; -use secrecy::{ExposeSecret,SecretVec,Secret}; +use secrecy::{ExposeSecret, Secret, SecretVec}; use std::collections::{HashMap, VecDeque}; use std::convert::{identity, Infallible}; use std::future::Future; @@ -18,15 +17,23 @@ use tonic::client::GrpcService; use tonic::transport::{Certificate, Channel, ClientTlsConfig, Endpoint}; use tonic::IntoRequest; use zcash_client_backend::data_api::chain::{BlockCache, CommitmentTreeRoot}; -use zcash_client_backend::data_api::wallet::{ConfirmationsPolicy, TargetHeight, create_proposed_transactions, propose_shielding, shield_transparent_funds}; +use zcash_client_backend::data_api::wallet::{ + create_proposed_transactions, propose_shielding, shield_transparent_funds, ConfirmationsPolicy, + TargetHeight, +}; +use zcash_client_backend::data_api::WalletCommitmentTrees; use zcash_client_backend::fees::{self, StandardFeeRule}; -use zcash_client_backend::proto::service::{GetSubtreeRootsArg, RawTransaction, TreeState, TxFilter}; +use zcash_client_backend::proto::service::{ + GetSubtreeRootsArg, RawTransaction, TreeState, TxFilter, +}; use zcash_client_backend::wallet::WalletTransparentOutput; use zcash_client_memory::MemBlockCache; use zcash_client_sqlite::error::SqliteClientError; use zcash_client_sqlite::util::SystemClock; use zcash_client_sqlite::{AccountUuid, WalletDb}; -use zcash_note_encryption::{try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk}; +use zcash_note_encryption::{ + try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ovk, +}; use zcash_primitives::transaction::builder::{BuildConfig, Builder as TxBuilder}; use zcash_primitives::transaction::components::TxOut; use zcash_primitives::transaction::fees::zip317::{self, MINIMUM_FEE}; @@ -65,9 +72,8 @@ use zcash_client_backend::{ self, chain::{error::Error as ChainError, scan_cached_blocks, ChainState}, scanning::{ScanPriority, ScanRange}, - Balance, - wallet, Account as APIAccount, AccountBirthday, AccountPurpose, WalletRead, WalletWrite, - Zip32Derivation, + wallet, Account as APIAccount, AccountBirthday, AccountPurpose, Balance, WalletRead, + WalletWrite, Zip32Derivation, }, encoding::AddressCodec, keys::{ @@ -81,12 +87,13 @@ use zcash_client_backend::{ }, }, }; +use zcash_primitives::transaction::{RosterMember, StakeTxId, StakingAction, StakingActionKind}; use zcash_protocol::consensus::{NetworkType, Parameters, MAIN_NETWORK, TEST_NETWORK}; +use zcash_protocol::local_consensus::LocalNetwork; use zcash_transparent::{ address::TransparentAddress, keys::{IncomingViewingKey, TransparentKeyScope}, }; -use zcash_primitives::transaction::{RosterMember, StakingAction, StakingActionKind, StakeTxId}; const CHEAT_UNSTAKING: bool = false; @@ -123,7 +130,14 @@ async fn wait_for_zainod() { } } -fn block_policy_10() -> ConfirmationsPolicy { ConfirmationsPolicy::new(std::num::NonZeroU32::new(5).unwrap(), std::num::NonZeroU32::new(5).unwrap(), false).unwrap() } +fn block_policy_10() -> ConfirmationsPolicy { + ConfirmationsPolicy::new( + std::num::NonZeroU32::new(5).unwrap(), + std::num::NonZeroU32::new(5).unwrap(), + false, + ) + .unwrap() +} #[derive(Debug, Clone, PartialEq)] enum WalletAction { @@ -147,16 +161,27 @@ pub enum WalletTxKind { pub struct WalletTx(pub TransactionSummary, pub WalletTxKind); impl WalletTx { - pub fn with_fake_data(kind: WalletTxKind, sent: u64, received: u64, shielding: bool, memo: &str, mined_height: u32) -> Self { + pub fn with_fake_data( + kind: WalletTxKind, + sent: u64, + received: u64, + shielding: bool, + memo: &str, + mined_height: u32, + ) -> Self { let mut memo_as_bytes = [0u8; 512]; &memo_as_bytes[0..memo.len()].copy_from_slice(memo.as_bytes()); Self( - TransactionSummary{ + TransactionSummary { account_id: AccountUuid::default(), txid: TxId::from_bytes([0; 32]), expiry_height: None, - mined_height: if mined_height != 0 { Some(BlockHeight::from_u32(mined_height)) } else { None }, + mined_height: if mined_height != 0 { + Some(BlockHeight::from_u32(mined_height)) + } else { + None + }, account_value_delta: ZatBalance::from_i64(-(sent as i64)).unwrap(), total_spent: Zatoshis::from_u64(sent).unwrap(), total_received: Zatoshis::from_u64(received).unwrap(), @@ -194,14 +219,19 @@ fn w_flip(use_i: &mut usize, update_i: &mut usize) { #[derive(Default, Debug, Clone)] pub struct WalletState { - pub balance: i64, // in zats + pub balance: i64, // in zats pub pending_balance: i64, // in zats - pub staked_balance: i64, // in zats + pub staked_balance: i64, // in zats pub show_staked_balance: bool, - pub txs: Vec, - pub roster: Vec, - pub staked_roster: Vec<([u8; 32] /* pub key */, [u8; 32] /* txid */, u64 /* initial */, u64 /* accumulated */)>, + pub txs: Vec, + pub roster: Vec, + pub staked_roster: Vec<( + [u8; 32], /* pub key */ + [u8; 32], /* txid */ + u64, /* initial */ + u64, /* accumulated */ + )>, pub waiting_for_faucet: bool, pub waiting_for_stake_to_finalizer: bool, @@ -228,45 +258,108 @@ impl WalletState { pub fn request_from_faucet(&mut self) { self.waiting_for_faucet = true; - if self.actions_in_flight.iter().filter(|a| match a { WalletAction::RequestFromFaucet => true, _ => false }).count() != 0 { + if self + .actions_in_flight + .iter() + .filter(|a| match a { + WalletAction::RequestFromFaucet => true, + _ => false, + }) + .count() + != 0 + { return; } - self.actions_in_flight.push_back(WalletAction::RequestFromFaucet); + self.actions_in_flight + .push_back(WalletAction::RequestFromFaucet); } pub fn stake_to_finalizer(&mut self, amount: u64, target_finalizer: [u8; 32]) { - if self.actions_in_flight.iter().filter(|a| match a { WalletAction::StakeToFinalizer(_,_) => true, _ => false }).count() != 0 { + if self + .actions_in_flight + .iter() + .filter(|a| match a { + WalletAction::StakeToFinalizer(_, _) => true, + _ => false, + }) + .count() + != 0 + { return; } self.waiting_for_stake_to_finalizer = true; - self.actions_in_flight.push_back(WalletAction::StakeToFinalizer(Zatoshis::from_u64(amount).expect("Invalid amount given to stake_to_finalizer"), target_finalizer)); + self.actions_in_flight + .push_back(WalletAction::StakeToFinalizer( + Zatoshis::from_u64(amount).expect("Invalid amount given to stake_to_finalizer"), + target_finalizer, + )); } pub fn unstake_from_finalizer(&mut self, txid: [u8; 32]) { let txid = TxId::from_bytes(txid); - if self.actions_in_flight.iter().filter(|a| match a { WalletAction::UnstakeFromFinalizer(id) if id.eq(&txid) => true, _ => false }).count() != 0 { + println!( + "===== UNSTAKE_FROM_FINALIZER with txid {}", + txid.to_string() + ); + if self + .actions_in_flight + .iter() + .filter(|a| match a { + WalletAction::UnstakeFromFinalizer(id) if id.eq(&txid) => true, + _ => false, + }) + .count() + != 0 + { return; } - self.actions_in_flight.push_back(WalletAction::UnstakeFromFinalizer(txid)); + self.actions_in_flight + .push_back(WalletAction::UnstakeFromFinalizer(txid)); } pub fn send_to_address(&mut self, address: String, amount: u64) { - let Ok(address) = UnifiedAddress::decode(&TEST_NETWORK /* @todo */, &address) else { + let Ok(address) = UnifiedAddress::decode( + &LocalNetwork { + overwinter: Some(BlockHeight::from_u32(1)), + sapling: Some(BlockHeight::from_u32(1)), + blossom: Some(BlockHeight::from_u32(1)), + heartwood: Some(BlockHeight::from_u32(1)), + canopy: Some(BlockHeight::from_u32(1)), + nu5: Some(BlockHeight::from_u32(1)), + nu6: Some(BlockHeight::from_u32(1)), + nu6_1: None, + }, /* @todo */ + &address, + ) else { println!("Invalid address for send: {}", address); return; }; - if self.actions_in_flight.iter().filter(|a| match a { - WalletAction::SendToAddress(addr, amt) if amt.into_u64() == amount && addr.eq(&address) => true, - _ => false - }).count() != 0 { + if self + .actions_in_flight + .iter() + .filter(|a| match a { + WalletAction::SendToAddress(addr, amt) + if amt.into_u64() == amount && addr.eq(&address) => + { + true + } + _ => false, + }) + .count() + != 0 + { return; } self.waiting_for_send = true; - self.actions_in_flight.push_back(WalletAction::SendToAddress(address, Zatoshis::from_u64(amount).expect("Invalid amount given to stake_to_finalizer"))); + self.actions_in_flight + .push_back(WalletAction::SendToAddress( + address, + Zatoshis::from_u64(amount).expect("Invalid amount given to stake_to_finalizer"), + )); } } @@ -292,10 +385,10 @@ impl Default for TxOptions { } pub async fn wallet_main(wallet_state: Arc>) { - fn stuff_from_seed_phrase(params:P, phrase: &str) -> ( - SecretVec, - UnifiedSpendingKey, - ) { + fn stuff_from_seed_phrase( + params: P, + phrase: &str, + ) -> (SecretVec, UnifiedSpendingKey) { use secrecy::ExposeSecret; let mnemonic = bip39::Mnemonic::parse(phrase).unwrap(); @@ -307,25 +400,37 @@ pub async fn wallet_main(wallet_state: Arc>) { let usk = UnifiedSpendingKey::from_seed(¶ms, seed.expose_secret(), account_id).unwrap(); let birthday = &AccountBirthday::from_parts( - ChainState::empty(BlockHeight::from_u32(0), zcash_primitives::block::BlockHash([0; 32])), + ChainState::empty( + BlockHeight::from_u32(0), + zcash_primitives::block::BlockHash([0; 32]), + ), None, ); (seed, usk) } - fn wallet_from_stuff(params: P, seed: SecretVec) -> ( + fn wallet_from_stuff( + params: P, + seed: SecretVec, + ) -> ( WalletDb, zcash_client_sqlite::wallet::Account, ) { - let mut wallet = zcash_client_sqlite::WalletDb::for_path(":memory:", params, SystemClock, OsRng).unwrap(); + let mut wallet = + zcash_client_sqlite::WalletDb::for_path(":memory:", params, SystemClock, OsRng) + .unwrap(); zcash_client_sqlite::wallet::init::init_wallet_db( &mut wallet, Some(Secret::new(seed.expose_secret().clone())), - ).unwrap(); + ) + .unwrap(); let birthday = &AccountBirthday::from_parts( - ChainState::empty(BlockHeight::from_u32(0), zcash_primitives::block::BlockHash([0; 32])), + ChainState::empty( + BlockHeight::from_u32(0), + zcash_primitives::block::BlockHash([0; 32]), + ), None, ); @@ -336,34 +441,58 @@ pub async fn wallet_main(wallet_state: Arc>) { (wallet, account) } - fn transparent_keys_from_usk(usk: &UnifiedSpendingKey, index: u32) -> Option<(secp256k1::PublicKey, secp256k1::SecretKey)> { + fn transparent_keys_from_usk( + usk: &UnifiedSpendingKey, + index: u32, + ) -> Option<(secp256k1::PublicKey, secp256k1::SecretKey)> { let transparent = usk.transparent(); let account_pubkey = transparent.to_account_pubkey(); let child_index = NonHardenedChildIndex::const_from_index(index); - let address_pubkey = account_pubkey.derive_address_pubkey(TransparentKeyScope::EXTERNAL, child_index).ok()?; + let address_pubkey = account_pubkey + .derive_address_pubkey(TransparentKeyScope::EXTERNAL, child_index) + .ok()?; let address_privkey = transparent.derive_external_secret_key(child_index).ok()?; Some((address_pubkey, address_privkey)) } - fn addrs_from_account(account: &zcash_client_sqlite::wallet::Account, index: u32) -> Option<(TransparentAddress, UnifiedAddress)> { + fn addrs_from_account( + account: &zcash_client_sqlite::wallet::Account, + index: u32, + ) -> Option<(TransparentAddress, UnifiedAddress)> { // NOTE: the wallet auto-increments the child index so this isn't recognized let ufvk = account.ufvk()?; - let (ua, di_) = ufvk.find_address(orchard::keys::DiversifierIndex::new(), UnifiedAddressRequest::ORCHARD).ok()?; + let (ua, di_) = ufvk + .find_address( + orchard::keys::DiversifierIndex::new(), + UnifiedAddressRequest::ORCHARD, + ) + .ok()?; let account_pubkey = ufvk.transparent()?; let child_index = NonHardenedChildIndex::const_from_index(index); - let address_pubkey = account_pubkey.derive_address_pubkey(TransparentKeyScope::EXTERNAL, child_index).ok()?; + let address_pubkey = account_pubkey + .derive_address_pubkey(TransparentKeyScope::EXTERNAL, child_index) + .ok()?; Some((TransparentAddress::from_pubkey(&address_pubkey), ua)) // Some(account.default_address().ok()??.0) } - let addrs_from_wallet = |wallet: &WalletDb<_, _, _, _>| -> Option<(TransparentAddress, UnifiedAddress)> { - let Ok(ids) = wallet.get_account_ids() else { return None; }; - let Some(id) = ids.first() else { return None; }; - let Ok(Some(account)) = wallet.get_account(*id) else { return None; }; - addrs_from_account(&account, 0) - }; + let addrs_from_wallet = + |wallet: &WalletDb<_, _, _, _>| -> Option<(TransparentAddress, UnifiedAddress)> { + let Ok(ids) = wallet.get_account_ids() else { + return None; + }; + let Some(id) = ids.first() else { + return None; + }; + let Ok(Some(account)) = wallet.get_account(*id) else { + return None; + }; + addrs_from_account(&account, 0) + }; - fn get_transaction_history(wallet: &zcash_client_sqlite::WalletDb) -> Result>, SqliteClientError> { + fn get_transaction_history( + wallet: &zcash_client_sqlite::WalletDb, + ) -> Result>, SqliteClientError> { let mut stmt = wallet.conn.prepare_cached( "SELECT accounts.uuid as account_uuid, v_transactions.* FROM v_transactions @@ -399,20 +528,40 @@ pub async fn wallet_main(wallet_state: Arc>) { .collect::, _>>()?; // lol - let mut pending: Vec<_> = results.iter().filter(|tx| tx.mined_height.is_none()).map(|tx| tx.clone()).collect(); - let mut mined: Vec<_> = results.iter().filter(|tx| tx.mined_height.is_some()).map(|tx| tx.clone()).collect(); - pending.sort_by(| a, b | a.txid.cmp(&b.txid)); + let mut pending: Vec<_> = results + .iter() + .filter(|tx| tx.mined_height.is_none()) + .map(|tx| tx.clone()) + .collect(); + let mut mined: Vec<_> = results + .iter() + .filter(|tx| tx.mined_height.is_some()) + .map(|tx| tx.clone()) + .collect(); + pending.sort_by(|a, b| a.txid.cmp(&b.txid)); mined.sort_by(|a, b| b.mined_height.unwrap().cmp(&a.mined_height.unwrap())); pending.extend_from_slice(&mined); return Ok(pending); } - async fn get_received_memos_and_actions(client: &mut CompactTxStreamerClient, wallet: &zcash_client_sqlite::WalletDb, params: P, history: &[TransactionSummary]) - -> Option<(HashMap, Vec)>, HashMap, Vec)>)> { - fn try_get_orchard_memos(tx: &TransactionData, ivk: &orchard::keys::PreparedIncomingViewingKey) -> Vec { + async fn get_received_memos_and_actions( + client: &mut CompactTxStreamerClient, + wallet: &zcash_client_sqlite::WalletDb, + params: P, + history: &[TransactionSummary], + ) -> Option<( + HashMap, Vec)>, + HashMap, Vec)>, + )> { + fn try_get_orchard_memos( + tx: &TransactionData, + ivk: &orchard::keys::PreparedIncomingViewingKey, + ) -> Vec { let mut memos = Vec::new(); - let Some(bundle) = tx.orchard_bundle() else { return memos; }; + let Some(bundle) = tx.orchard_bundle() else { + return memos; + }; for action in bundle.actions() { let domain = orchard::note_encryption::OrchardDomain::for_action(action); @@ -428,13 +577,24 @@ pub async fn wallet_main(wallet_state: Arc>) { return memos; } - fn try_get_orchard_sent_memos(tx: &TransactionData, ovk: &orchard::keys::OutgoingViewingKey) -> Vec { + fn try_get_orchard_sent_memos( + tx: &TransactionData, + ovk: &orchard::keys::OutgoingViewingKey, + ) -> Vec { let mut memos = Vec::new(); - let Some(bundle) = tx.orchard_bundle() else { return memos; }; + let Some(bundle) = tx.orchard_bundle() else { + return memos; + }; for action in bundle.actions() { let domain = orchard::note_encryption::OrchardDomain::for_action(action); - if let Some((_, _, memo)) = try_output_recovery_with_ovk(&domain, ovk, action, action.cv_net(), &action.encrypted_note().out_ciphertext) { + if let Some((_, _, memo)) = try_output_recovery_with_ovk( + &domain, + ovk, + action, + action.cv_net(), + &action.encrypted_note().out_ciphertext, + ) { let memo = String::from_utf8_lossy(&memo[..]); if memo.len() == 0 { continue; @@ -451,31 +611,37 @@ pub async fn wallet_main(wallet_state: Arc>) { let mut out_txid_map = HashMap::new(); let txids: Vec = history.iter().map(|h| h.txid).collect(); - let Ok(ids) = wallet.get_account_ids() else { return None; }; + let Ok(ids) = wallet.get_account_ids() else { + return None; + }; let accounts: Vec = ids .into_iter() .map(|id| wallet.get_account(id)) .filter_map(|acc| acc.ok()) .filter_map(|acc| acc) .collect(); - let uivks: Vec = accounts - .clone() - .into_iter() - .map(|acc| acc.uivk()) - .collect(); + let uivks: Vec = + accounts.clone().into_iter().map(|acc| acc.uivk()).collect(); let ufvks: Vec> = accounts .into_iter() - .map(|acc| acc.ufvk().map(|a|a.clone())) + .map(|acc| acc.ufvk().map(|a| a.clone())) .collect(); for txid in &txids { - let filter = TxFilter{ hash: txid.as_ref().to_vec(), ..Default::default() }; - let Ok(rawtx) = client.get_transaction(filter).await else { continue; }; + let filter = TxFilter { + hash: txid.as_ref().to_vec(), + ..Default::default() + }; + let Ok(rawtx) = client.get_transaction(filter).await else { + continue; + }; let rawtx = rawtx.into_inner(); let block_height = BlockHeight::from_u32(rawtx.height as u32); - let Ok(tx) = Transaction::read(&*rawtx.data, BranchId::for_height(¶ms, block_height)) else { + let Ok(tx) = + Transaction::read(&*rawtx.data, BranchId::for_height(¶ms, block_height)) + else { continue; }; @@ -483,7 +649,11 @@ pub async fn wallet_main(wallet_state: Arc>) { let mut memos = Vec::new(); let txdata = &tx.into_data(); for uivk in &uivks { - let possible_orchard_ivk = if let Some(orchard_ivk) = uivk.orchard() { Some(orchard_ivk.prepare()) } else { None }; + let possible_orchard_ivk = if let Some(orchard_ivk) = uivk.orchard() { + Some(orchard_ivk.prepare()) + } else { + None + }; if let Some(orchard_ivk) = possible_orchard_ivk { let m: Vec = try_get_orchard_memos(txdata, &orchard_ivk) .iter() @@ -497,11 +667,14 @@ pub async fn wallet_main(wallet_state: Arc>) { } txid_map.insert(*txid, (action.clone(), memos)); - let mut memos = Vec::new(); for ufvk in &ufvks { - let Some(ufvk) = ufvk else { continue; }; - let Some(ufvk_orchard) = ufvk.orchard() else { continue; }; + let Some(ufvk) = ufvk else { + continue; + }; + let Some(ufvk_orchard) = ufvk.orchard() else { + continue; + }; let ovk = ufvk_orchard.to_ovk(orchard::keys::Scope::External); let m: Vec = try_get_orchard_sent_memos(txdata, &ovk) .iter() @@ -520,12 +693,16 @@ pub async fn wallet_main(wallet_state: Arc>) { Some((txid_map, out_txid_map)) } - struct Timer { t_bgn: std::time::Instant, name: &'static str }; + struct Timer { + t_bgn: std::time::Instant, + name: &'static str, + }; impl Timer { pub fn scope(name: &'static str) -> Self { println!("started {}", name); Self { - name, t_bgn: std::time::Instant::now() + name, + t_bgn: std::time::Instant::now(), } } }; @@ -535,15 +712,29 @@ pub async fn wallet_main(wallet_state: Arc>) { } } - let send_zats = async |client: &mut CompactTxStreamerClient<_>, dst_ua: &UnifiedAddress, src_wallet: &mut WalletDb<_, _, _, _>, src_usk: &UnifiedSpendingKey, zats: Zatoshis, params, opts: &TxOptions| -> Option<[u8;32]> { + let send_zats = async |client: &mut CompactTxStreamerClient<_>, + dst_ua: &UnifiedAddress, + src_wallet: &mut WalletDb<_, _, _, _>, + src_usk: &UnifiedSpendingKey, + zats: Zatoshis, + params, + opts: &TxOptions| + -> Option<[u8; 32]> { let t = Timer::scope("send_zats"); // @todo(judah): handle multiple accounts? - let Ok(src_ids) = src_wallet.get_account_ids() else { return None; }; - let Some(src_id) = src_ids.first() else { return None; }; - let Ok(Some(src_account)) = src_wallet.get_account(*src_id) else { return None; }; + let Ok(src_ids) = src_wallet.get_account_ids() else { + return None; + }; + let Some(src_id) = src_ids.first() else { + return None; + }; + let Ok(Some(src_account)) = src_wallet.get_account(*src_id) else { + return None; + }; - const FALLBACK_CHANGE_POOL: zcash_protocol::ShieldedProtocol = zcash_protocol::ShieldedProtocol::Orchard; + const FALLBACK_CHANGE_POOL: zcash_protocol::ShieldedProtocol = + zcash_protocol::ShieldedProtocol::Orchard; match wallet::propose_standard_transfer_to_address::<_, _, Infallible>( src_wallet, @@ -555,12 +746,12 @@ pub async fn wallet_main(wallet_state: Arc>) { zats, opts.memo.clone(), None, - FALLBACK_CHANGE_POOL) - { + FALLBACK_CHANGE_POOL, + ) { Err(err) => { println!("propose_transfer error: {err:?}"); None - }, + } Ok(proposal) => { let prover = LocalTxProver::bundled(); match wallet::create_proposed_transactions::<_, _, Infallible, _, Infallible, _>( @@ -576,7 +767,7 @@ pub async fn wallet_main(wallet_state: Arc>) { Err(err) => { println!("create_proposed_transactions error: {err:?}"); None - }, + } Ok(txids) => { if txids.len() > 1 { println!("Unexpectedly created {} transactions", txids.len()); @@ -590,7 +781,9 @@ pub async fn wallet_main(wallet_state: Arc>) { } Ok(Some(tx)) => tx, Ok(None) => { - println!("failed to get tx {txid:?} immediately after making it: (None)"); + println!( + "failed to get tx {txid:?} immediately after making it: (None)" + ); return None; } }; @@ -603,7 +796,7 @@ pub async fn wallet_main(wallet_state: Arc>) { let raw_tx = RawTransaction { data, height: 0 }; match client.send_transaction(raw_tx).await { - Ok(res) => println!("sent transaction: {res:?}"), + Ok(res) => println!("sent transaction: {res:?}"), Err(err) => { return None; } @@ -617,20 +810,43 @@ pub async fn wallet_main(wallet_state: Arc>) { } }; - let send_zats_to_wallet = async |client: &mut CompactTxStreamerClient<_>, dst_wallet: &mut WalletDb<_, _, _, _>, src_wallet: &mut WalletDb<_, _, _, _>, src_usk: &UnifiedSpendingKey, zats: Zatoshis, params, opts: &TxOptions| -> Option<[u8;32]> { + let send_zats_to_wallet = async |client: &mut CompactTxStreamerClient<_>, + dst_wallet: &mut WalletDb<_, _, _, _>, + src_wallet: &mut WalletDb<_, _, _, _>, + src_usk: &UnifiedSpendingKey, + zats: Zatoshis, + params, + opts: &TxOptions| + -> Option<[u8; 32]> { match addrs_from_wallet(dst_wallet) { - Some((_, dst_ua)) => send_zats(client, &dst_ua, src_wallet, src_usk, zats, params, opts).await, + Some((_, dst_ua)) => { + send_zats(client, &dst_ua, src_wallet, src_usk, zats, params, opts).await + } None => None, } }; - let send_unstake_reward = async |client: &mut CompactTxStreamerClient<_>, roster: &[RosterMember], txid_map: &HashMap, Vec)>, txid: &TxId, src_wallet: &mut WalletDb<_, _, _, _>, src_usk: &UnifiedSpendingKey, params, thing: &mut Vec::| -> Option<[u8;32]> { + let send_unstake_reward = async |client: &mut CompactTxStreamerClient<_>, + roster: &[RosterMember], + txid_map: &HashMap< + TxId, + (Option, Vec), + >, + txid: &TxId, + src_wallet: &mut WalletDb<_, _, _, _>, + src_usk: &UnifiedSpendingKey, + params, + thing: &mut Vec| + -> Option<[u8; 32]> { println!("SENDING UNSTAKE REWARD"); let Some(staked_txid) = ('find_txid: { for mem in roster { + println!("== MEMBER WITH TXS: {mem:?}"); for mem_txid in &mem.txids { + println!("Comparing {mem_txid:?} to {txid:?}"); if TxId::from_bytes(mem_txid.txid) == *txid { + println!("Found member with txid: {mem_txid:?}!!!"); break 'find_txid Some(mem_txid.clone()); } } @@ -642,28 +858,63 @@ pub async fn wallet_main(wallet_state: Arc>) { }; let Some((action, memos)) = txid_map.get(&txid) else { - println!("*** Failed to find miner staking transaction via txid {:?}", txid); + println!( + "*** Failed to find miner staking transaction via txid {:?}", + txid + ); return None; }; - let Some(destination_address) = memos.iter().find(|memo| memo.starts_with("utest")) else { - println!("*** Failed to find destination address memo in txid {:?}", txid); + let Some(destination_address) = memos.iter().find(|memo| memo.starts_with("uregtest")) + else { + println!( + "*** Failed to find destination address memo in txid {:?}", + txid + ); return None; }; let destination_address = destination_address.trim_end_matches(|c| c == '\0'); - let Ok(destination_ua) = UnifiedAddress::decode(params, destination_address) else { - println!("*** Failed to decode destination address {:?}", destination_address); + let Ok(destination_ua) = UnifiedAddress::decode( + &LocalNetwork { + overwinter: Some(BlockHeight::from_u32(1)), + sapling: Some(BlockHeight::from_u32(1)), + blossom: Some(BlockHeight::from_u32(1)), + heartwood: Some(BlockHeight::from_u32(1)), + canopy: Some(BlockHeight::from_u32(1)), + nu5: Some(BlockHeight::from_u32(1)), + nu6: Some(BlockHeight::from_u32(1)), + nu6_1: None, + }, + destination_address, + ) else { + println!( + "*** Failed to decode destination address {:?}", + destination_address + ); return None; }; - let memo_str = &format!("@UNSTAKE_RECEIVE: {}\nThanks for staking!", StakingAction::str_from_addr(staked_txid.txid)); - let options = TxOptions{ + let memo_str = &format!( + "@UNSTAKE_RECEIVE: {}\nThanks for staking!", + StakingAction::str_from_addr(staked_txid.txid) + ); + let options = TxOptions { memo: Some(zcash_protocol::memo::MemoBytes::from_bytes(memo_str.as_bytes()).unwrap()), ..Default::default() }; - match send_zats(client, &destination_ua, src_wallet, &src_usk, Zatoshis::from_u64(staked_txid.zats).unwrap(), params, &options).await { + match send_zats( + client, + &destination_ua, + src_wallet, + &src_usk, + Zatoshis::from_u64(staked_txid.zats).unwrap(), + params, + &options, + ) + .await + { None => { println!("Failed to send reward to user"); None @@ -701,7 +952,8 @@ pub async fn wallet_main(wallet_state: Arc>) { let (seed, miner_usk) = stuff_from_seed_phrase(network, "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" ); - let (miner_wallet, miner_account) = wallet_from_stuff(network, Secret::new(seed.expose_secret().clone())); + let (miner_wallet, miner_account) = + wallet_from_stuff(network, Secret::new(seed.expose_secret().clone())); let (miner_t_addr, miner_ua) = addrs_from_account(&miner_account, 0).unwrap(); let miner_t_addr_str = miner_t_addr.encode(network); @@ -709,7 +961,18 @@ pub async fn wallet_main(wallet_state: Arc>) { let miner_t_recs = miner_wallet .get_transparent_receivers(miner_account.id(), false, false) .unwrap(); - (miner_wallet, miner_account, seed, miner_usk, miner_pubkey, miner_privkey, miner_t_addr, miner_ua, HashMap::, Vec)>::new(), HashMap::, Vec)>::new()) + ( + miner_wallet, + miner_account, + seed, + miner_usk, + miner_pubkey, + miner_privkey, + miner_t_addr, + miner_ua, + HashMap::, Vec)>::new(), + HashMap::, Vec)>::new(), + ) }; let ( @@ -724,11 +987,17 @@ pub async fn wallet_main(wallet_state: Arc>) { mut user_txid_map, ) = { // roundtrip seed through mnemonic phrase - let mnemonic = bip39::Mnemonic::from_entropy_in(bip39::Language::English, &global_seed).unwrap(); - let phrase = mnemonic.words().map(|s| s.to_string()).collect::>().join(" "); + let mnemonic = + bip39::Mnemonic::from_entropy_in(bip39::Language::English, &global_seed).unwrap(); + let phrase = mnemonic + .words() + .map(|s| s.to_string()) + .collect::>() + .join(" "); let (seed, user_usk) = stuff_from_seed_phrase(network, &phrase); - let (user_wallet, user_account) = wallet_from_stuff(network, Secret::new(seed.expose_secret().clone())); + let (user_wallet, user_account) = + wallet_from_stuff(network, Secret::new(seed.expose_secret().clone())); let (user_t_addr, user_ua) = addrs_from_account(&user_account, 0).unwrap(); let user_t_addr_str = user_t_addr.encode(network); let (user_pubkey, user_privkey) = transparent_keys_from_usk(&user_usk, 0).unwrap(); @@ -740,20 +1009,44 @@ pub async fn wallet_main(wallet_state: Arc>) { // NOTE: the default isn't the same as below, but I think this is because it forces a diversifier index // println!("User wallet: {}/{:?}", user_t_addr_str, user_t_addr1.encode(network)); - (user_wallet, user_account, seed, user_usk, user_pubkey, user_privkey, user_t_addr, user_ua, HashMap::new()) + ( + user_wallet, + user_account, + seed, + user_usk, + user_pubkey, + user_privkey, + user_t_addr, + user_ua, + HashMap::new(), + ) }; + let miner_ua_regtest = miner_ua.encode(&LocalNetwork { + overwinter: Some(BlockHeight::from_u32(1)), + sapling: Some(BlockHeight::from_u32(1)), + blossom: Some(BlockHeight::from_u32(1)), + heartwood: Some(BlockHeight::from_u32(1)), + canopy: Some(BlockHeight::from_u32(1)), + nu5: Some(BlockHeight::from_u32(1)), + nu6: Some(BlockHeight::from_u32(1)), + nu6_1: None, + }); + let user_ua_str = user_ua.encode(network); println!("*************************"); println!("MINER WALLET T-ADDRESS: {}", miner_ua.encode(network)); - println!("MINER WALLET ADDRESS: {}", miner_t_address.encode(network)); + println!( + "MINER WALLET ADDRESS: {}", + miner_t_address.encode(network) + ); + println!("MINER WALLET ADDRESS (REGTEST): {}", miner_ua_regtest); println!("USER WALLET T-ADDRESS: {}", user_t_address.encode(network)); println!("USER WALLET ADDRESS: {}", user_ua_str); println!("*************************"); wallet_state.lock().unwrap().user_recv_ua = user_ua_str; - println!("waiting for zaino to be ready..."); wait_for_zainod().await; ////////////////////////////////////////////////////////////////////////////////// @@ -765,27 +1058,38 @@ pub async fn wallet_main(wallet_state: Arc>) { // @todo(judah): investigate why requests get randomly dropped in a strange way: // transport error, service not ready, etc. let mut client = loop { - if let Ok(channel) = Channel::from_static("http://localhost:18233").connect().await { + if let Ok(channel) = Channel::from_static("http://localhost:18233") + .connect() + .await + { break CompactTxStreamerClient::new(channel); } tokio::time::sleep(std::time::Duration::from_millis(1)).await; }; - let mut time_since_last_transparent_shielded = std::time::Instant::now() - std::time::Duration::from_secs(1000); + let mut time_since_last_transparent_shielded = + std::time::Instant::now() - std::time::Duration::from_secs(1000); - let (mut user_use_i, mut user_update_i) = (0,0); - let (mut miner_use_i, mut miner_update_i) = (0,0); - let mut user_wallets = [user_wallet_init, WalletDb::for_path(":memory:", network, SystemClock, OsRng).unwrap()]; - let mut miner_wallets = [miner_wallet_init, WalletDb::for_path(":memory:", network, SystemClock, OsRng).unwrap()]; + let (mut user_use_i, mut user_update_i) = (0, 0); + let (mut miner_use_i, mut miner_update_i) = (0, 0); + let mut user_wallets = [ + user_wallet_init, + WalletDb::for_path(":memory:", network, SystemClock, OsRng).unwrap(), + ]; + let mut miner_wallets = [ + miner_wallet_init, + WalletDb::for_path(":memory:", network, SystemClock, OsRng).unwrap(), + ]; - let mut stupid_thing_because_judah_is_tired_and_wants_this_to_work_properly = Vec::::new(); + let mut stupid_thing_because_judah_is_tired_and_wants_this_to_work_properly = + Vec::::new(); loop { - match client.get_roster(Empty{}).await { + match client.get_roster(Empty {}).await { Err(err) => println!("Get roster error: {err:?}"), Ok(res) => { - use std::io::{ Cursor,Read }; + use std::io::{Cursor, Read}; let roster_bytes = res.into_inner().data; let mut ok = roster_bytes.len() > 0; @@ -794,7 +1098,11 @@ pub async fn wallet_main(wallet_state: Arc>) { let mut new_roster = Vec::new(); let mut num_buf = [0u8; 8]; 'read: while cur.position() < roster_bytes.len() as u64 { - let mut m = RosterMember{ pub_key: [0;32], voting_power:0, txids: Vec::new() }; + let mut m = RosterMember { + pub_key: [0; 32], + voting_power: 0, + txids: Vec::new(), + }; if let Err(err) = cur.read_exact(&mut m.pub_key) { println!("******* ROSTER DESERIALIZE ERROR: {err:?}"); ok = false; @@ -816,7 +1124,10 @@ pub async fn wallet_main(wallet_state: Arc>) { let mut voting_power_check = 0; let txids_n = u64::from_le_bytes(num_buf); for _ in 0..txids_n { - let mut stake_txid = StakeTxId{ txid:[0;32], zats:0 }; + let mut stake_txid = StakeTxId { + txid: [0; 32], + zats: 0, + }; if let Err(err) = cur.read_exact(&mut stake_txid.txid) { println!("******* ROSTER DESERIALIZE ERROR: {err:?}"); ok = false; @@ -834,7 +1145,10 @@ pub async fn wallet_main(wallet_state: Arc>) { if m.voting_power != voting_power_check { // TODO: use manually-found one? - println!("******* RECEIVED ROSTER VOTING POWER INACCURATE: {} vs {}", m.voting_power, voting_power_check); + println!( + "******* RECEIVED ROSTER VOTING POWER INACCURATE: {} vs {}", + m.voting_power, voting_power_check + ); // ok = false; // break; } @@ -848,10 +1162,10 @@ pub async fn wallet_main(wallet_state: Arc>) { let wallet_roster = roster .iter() - .map(|member| WalletRosterMember{ + .map(|member| WalletRosterMember { pub_key: member.pub_key, voting_power: member.voting_power, - txids: member.txids.clone() + txids: member.txids.clone(), }) .collect::>() .clone(); @@ -872,7 +1186,8 @@ pub async fn wallet_main(wallet_state: Arc>) { if network_tip_height == u64::from(chain_height) { w_flip(&mut miner_use_i, &mut miner_update_i); println!("DOUBLE WALLET: flipping miner to {miner_use_i} at height {network_tip_height}"); - (miner_wallets[miner_update_i], miner_account) = wallet_from_stuff(network, Secret::new(miner_seed.expose_secret().clone())); + (miner_wallets[miner_update_i], miner_account) = + wallet_from_stuff(network, Secret::new(miner_seed.expose_secret().clone())); } } } @@ -882,15 +1197,24 @@ pub async fn wallet_main(wallet_state: Arc>) { if network_tip_height == u64::from(chain_height) { w_flip(&mut user_use_i, &mut user_update_i); println!("DOUBLE WALLET: flipping user to {user_use_i} at height {network_tip_height}"); - (user_wallets[user_update_i], user_account) = wallet_from_stuff(network, Secret::new(user_seed.expose_secret().clone())); + (user_wallets[user_update_i], user_account) = + wallet_from_stuff(network, Secret::new(user_seed.expose_secret().clone())); } } } // Sync wallet DBs for (wallets, t_address, idxs) in [ - (&mut miner_wallets, miner_t_address, [miner_use_i, miner_update_i]), - (&mut user_wallets, user_t_address, [user_use_i, user_update_i]), + ( + &mut miner_wallets, + miner_t_address, + [miner_use_i, miner_update_i], + ), + ( + &mut user_wallets, + user_t_address, + [user_use_i, user_update_i], + ), ] { for i_i in 0..idxs.len() { let i = idxs[i_i]; @@ -910,17 +1234,28 @@ pub async fn wallet_main(wallet_state: Arc>) { } else { true } - } - { + } { const MAX_BLOCKS_TO_DOWNLOAD_AT_TIME: u32 = 64; - if let Err(err) = zcash_client_backend::sync::run(&mut client, network, &mut block_cache, wallet, MAX_BLOCKS_TO_DOWNLOAD_AT_TIME).await { + if let Err(err) = zcash_client_backend::sync::run( + &mut client, + network, + &mut block_cache, + wallet, + MAX_BLOCKS_TO_DOWNLOAD_AT_TIME, + ) + .await + { println!("Failed to sync wallet: {}", err); continue; } } - let Ok(summary) = wallet.get_wallet_summary(block_policy_10()) else { continue; }; - let Some(summary) = summary else { continue; }; + let Ok(summary) = wallet.get_wallet_summary(block_policy_10()) else { + continue; + }; + let Some(summary) = summary else { + continue; + }; let balances = summary.account_balances(); println!("******* WALLET {:?} *******", t_address.encode(network)); @@ -929,7 +1264,10 @@ pub async fn wallet_main(wallet_state: Arc>) { } } - let (user_wallet, miner_wallet) = (&mut user_wallets[user_use_i], &mut miner_wallets[miner_use_i]); + let (user_wallet, miner_wallet) = ( + &mut user_wallets[user_use_i], + &mut miner_wallets[miner_use_i], + ); if time_since_last_transparent_shielded.elapsed().as_secs() > 15 { // Shield miner's transparent ZATOSHIz (async | wallet: &mut WalletDb<_, _, _, _>, account: &zcash_client_sqlite::wallet::Account, usk: &UnifiedSpendingKey | { @@ -1314,41 +1652,73 @@ pub async fn wallet_main(wallet_state: Arc>) { (async |miner_wallet: &mut WalletDb<_, _, _, _>, miner_usk, network| { if let Ok(history) = get_transaction_history(miner_wallet) { - if let Some((map, sent_map)) = get_received_memos_and_actions(&mut client, miner_wallet, network, &history).await { + if let Some((map, sent_map)) = + get_received_memos_and_actions(&mut client, miner_wallet, network, &history) + .await + { miner_txid_map = map; miner_sent_txid_map = sent_map; if !CHEAT_UNSTAKING && *AM_I_THE_UNSTAKER.lock().unwrap() { // TODO: does this have a race condition with syncing? for (tx, (action, memos)) in &miner_txid_map { - let Some(action) = action else { continue; }; - if action.kind != StakingActionKind::Sub { continue; } + let Some(action) = action else { + continue; + }; + if action.kind != StakingActionKind::Sub { + continue; + } println!("SAM DEBUG got here 1"); let mut handled = false; // got a request to unstake; have we already repaid it? - for (sent_tx, (_action, sent_memos)) in &miner_sent_txid_map { + for (sent_tx, (_action, sent_memos)) in &miner_sent_txid_map { println!("SAM DEBUG got here 2"); - if sent_memos.len() == 0 { continue; } - if sent_memos[0].len() < "@UNSTAKE_RECEIVE: ".len()+64 { continue; } + if sent_memos.len() == 0 { + continue; + } + if sent_memos[0].len() < "@UNSTAKE_RECEIVE: ".len() + 64 { + continue; + } println!("SAM DEBUG got here 3"); - if Some(action.source) == StakingAction::addr_from_str_bytes(&sent_memos[0].as_bytes()["@UNSTAKE_RECEIVE: ".len().."@UNSTAKE_RECEIVE: ".len()+64]) { + if Some(action.source) + == StakingAction::addr_from_str_bytes( + &sent_memos[0].as_bytes()["@UNSTAKE_RECEIVE: ".len() + .."@UNSTAKE_RECEIVE: ".len() + 64], + ) + { println!("SAM DEBUG got here 4"); handled = true; break; } } println!("SAM DEBUG got here 5"); + println!("action: {action:?}, handled: {handled}"); + println!( + "Source txid: {}", + TxId::from_bytes(action.source).to_string() + ); if !handled { println!("SAM DEBUG"); - send_unstake_reward(&mut client, &roster, &miner_txid_map, &TxId::from_bytes(action.source), miner_wallet, miner_usk, network, &mut Vec::new()).await; + send_unstake_reward( + &mut client, + &roster, + &miner_txid_map, + &TxId::from_bytes(action.source), + miner_wallet, + miner_usk, + network, + &mut Vec::new(), + ) + .await; } } } } } - })(miner_wallet, &miner_usk, network).await; + })(miner_wallet, &miner_usk, network) + .await; // Process gui wallet actions // @todo(judah): I'm thinking the weird frame hitch we get in the UI is caused by this loop, @@ -1368,8 +1738,13 @@ pub async fn wallet_main(wallet_state: Arc>) { } }; - println!("*** wallet has {:?} actions in flight", wallet_state.actions_in_flight.len()); - let Some(action) = wallet_state.actions_in_flight.front() else { break; }; + println!( + "*** wallet has {:?} actions in flight", + wallet_state.actions_in_flight.len() + ); + let Some(action) = wallet_state.actions_in_flight.front() else { + break; + }; action.clone() }; @@ -1378,11 +1753,29 @@ pub async fn wallet_main(wallet_state: Arc>) { WalletAction::RequestFromFaucet => { // NOTE: we can't send transparent->transparent through the high-level API, we // have to propose_shielding first, then send in a later block - let zats = (Zatoshis::from_nonnegative_i64(500_000_000).unwrap() - MINIMUM_FEE).unwrap(); - match send_zats_to_wallet(&mut client, user_wallet, miner_wallet, &miner_usk, zats, network, &TxOptions{ - memo: Some(zcash_protocol::memo::MemoBytes::from_bytes("Happy spending, with love from your favourite faucet".as_bytes()).unwrap()), - ..TxOptions::default() - }).await { + let zats = (Zatoshis::from_nonnegative_i64(500_000_000).unwrap() + - MINIMUM_FEE) + .unwrap(); + match send_zats_to_wallet( + &mut client, + user_wallet, + miner_wallet, + &miner_usk, + zats, + network, + &TxOptions { + memo: Some( + zcash_protocol::memo::MemoBytes::from_bytes( + "Happy spending, with love from your favourite faucet" + .as_bytes(), + ) + .unwrap(), + ), + ..TxOptions::default() + }, + ) + .await + { None => { wallet_state.lock().unwrap().waiting_for_faucet = false; true @@ -1390,12 +1783,14 @@ pub async fn wallet_main(wallet_state: Arc>) { Some(_) => { wallet_state.lock().unwrap().waiting_for_faucet = false; true - }, + } } } WalletAction::StakeToFinalizer(amount, target_finalizer) => { - let Ok(Some(wallet_summary)) = user_wallet.get_wallet_summary(ConfirmationsPolicy::MIN) else { + let Ok(Some(wallet_summary)) = + user_wallet.get_wallet_summary(ConfirmationsPolicy::MIN) + else { println!("Failed to get wallet summary"); break 'process_action false; }; @@ -1413,7 +1808,10 @@ pub async fn wallet_main(wallet_state: Arc>) { break 'process_action false; } - println!("********** STAKING ZEC {:?} ({:?}) TO THE MINER but also to {:?}", amount, amount_with_fee, target_finalizer); + println!( + "********** STAKING ZEC {:?} ({:?}) TO THE MINER but also to {:?}", + amount, amount_with_fee, target_finalizer + ); let opts = TxOptions { staking_action: Some(StakingAction { kind: StakingActionKind::Add, @@ -1423,10 +1821,25 @@ pub async fn wallet_main(wallet_state: Arc>) { insecure_target_name: "".to_owned(), insecure_source_name: "".to_owned(), }), - memo: Some(zcash_protocol::memo::MemoBytes::from_bytes(user_ua.encode(network).to_string().as_bytes()).unwrap()), + memo: Some( + zcash_protocol::memo::MemoBytes::from_bytes( + user_ua.encode(network).to_string().as_bytes(), + ) + .unwrap(), + ), }; - match send_zats_to_wallet(&mut client, miner_wallet, user_wallet, &user_usk, amount_with_fee, network, &opts).await { + match send_zats_to_wallet( + &mut client, + miner_wallet, + user_wallet, + &user_usk, + amount_with_fee, + network, + &opts, + ) + .await + { None => { println!("Failed to send ZEC to miner"); wallet_state.lock().unwrap().waiting_for_stake_to_finalizer = false; @@ -1440,7 +1853,9 @@ pub async fn wallet_main(wallet_state: Arc>) { } WalletAction::SendToAddress(address, amount) => { - let Ok(Some(wallet_summary)) = user_wallet.get_wallet_summary(ConfirmationsPolicy::MIN) else { + let Ok(Some(wallet_summary)) = + user_wallet.get_wallet_summary(ConfirmationsPolicy::MIN) + else { println!("Failed to get wallet summary"); break 'process_action false; }; @@ -1458,8 +1873,23 @@ pub async fn wallet_main(wallet_state: Arc>) { break 'process_action false; } - println!("*********** SEND ZEC {:?} ({:?}) TO {}", amount, amount_with_fee, &address.encode(network)); - match send_zats(&mut client, &address, user_wallet, &user_usk, amount_with_fee, network, &TxOptions::default()).await { + println!( + "*********** SEND ZEC {:?} ({:?}) TO {}", + amount, + amount_with_fee, + &address.encode(network) + ); + match send_zats( + &mut client, + &address, + user_wallet, + &user_usk, + amount_with_fee, + network, + &TxOptions::default(), + ) + .await + { None => { println!("Failed to send ZEC to {}", address.encode(network)); wallet_state.lock().unwrap().waiting_for_send = false; @@ -1473,10 +1903,14 @@ pub async fn wallet_main(wallet_state: Arc>) { } WalletAction::UnstakeFromFinalizer(txid) => { - let mut ok = { // User sends unstaking action + let mut ok = { + // User sends unstaking action + println!("transactions"); let Some((member_pub_key, staked_txid)) = ('find_txid: { for mem in &roster { for mem_txid in &mem.txids { + println!("==MEMBER TXID (in roster): {mem_txid:?}"); + if TxId::from_bytes(mem_txid.txid) == *txid { break 'find_txid Some((mem.pub_key, mem_txid.clone())); } @@ -1489,7 +1923,10 @@ pub async fn wallet_main(wallet_state: Arc>) { }; let Some((action, _)) = user_txid_map.get(&txid) else { - println!("*** Failed to find user staking transaction via txid {:?}", txid); + println!( + "*** Failed to find user staking transaction via txid {:?}", + txid + ); break 'process_action false; }; @@ -1512,7 +1949,17 @@ pub async fn wallet_main(wallet_state: Arc>) { // @note(judah): the miner sends to its own address because if the user sends it, // the tx will appear as a regular send of -0.2 cTAZ.... - match send_zats(&mut client, &miner_ua, miner_wallet, &miner_usk, Zatoshis::from_u64(10_000).unwrap() /* @todo fees */, network, &opts).await { + match send_zats( + &mut client, + &miner_ua, + miner_wallet, + &miner_usk, + Zatoshis::from_u64(10_000).unwrap(), /* @todo fees */ + network, + &opts, + ) + .await + { None => { println!("Failed to send unstaking action to miner"); false @@ -1532,7 +1979,7 @@ pub async fn wallet_main(wallet_state: Arc>) { ok } - _ => { true } + _ => true, } }; diff --git a/zebra-crosslink/zebra-crosslink/src/lib.rs b/zebra-crosslink/zebra-crosslink/src/lib.rs index 1e660674..75245e1c 100644 --- a/zebra-crosslink/zebra-crosslink/src/lib.rs +++ b/zebra-crosslink/zebra-crosslink/src/lib.rs @@ -13,7 +13,7 @@ use strum::{EnumCount, IntoEnumIterator}; use strum_macros::{EnumCount, EnumIter}; use tenderlink::SortedRosterMember; -use zcash_primitives::transaction::{RosterMember, StakingAction, StakingActionKind, StakeTxId}; +use zcash_primitives::transaction::{RosterMember, StakeTxId, StakingAction, StakingActionKind}; use zebra_chain::serialization::{ SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize, }; @@ -188,12 +188,20 @@ pub(crate) struct TFLServiceInternal { current_bc_final: Option<(BlockHeight, BlockHash)>, } -fn call_from_state_to_crosslink_to_ask_about_fat_pointers(internal_handle: &TFLServiceHandle, parent_fat_pointer: zebra_chain::block::FatPointerToBftBlock, child_fat_pointer: zebra_chain::block::FatPointerToBftBlock) -> bool { +fn call_from_state_to_crosslink_to_ask_about_fat_pointers( + internal_handle: &TFLServiceHandle, + parent_fat_pointer: zebra_chain::block::FatPointerToBftBlock, + child_fat_pointer: zebra_chain::block::FatPointerToBftBlock, +) -> bool { let mut internal = internal_handle.internal.blocking_lock(); let parent_height = if parent_fat_pointer == zebra_chain::block::FatPointerToBftBlock::null() { 0 } else { - if let Some(h) = internal.bft_blocks.iter().position(|b| b.blake3_hash().0 == parent_fat_pointer.points_at_block_hash()) { + if let Some(h) = internal + .bft_blocks + .iter() + .position(|b| b.blake3_hash().0 == parent_fat_pointer.points_at_block_hash()) + { h } else { return false; @@ -202,7 +210,11 @@ fn call_from_state_to_crosslink_to_ask_about_fat_pointers(internal_handle: &TFLS let child_height = if child_fat_pointer == zebra_chain::block::FatPointerToBftBlock::null() { 0 } else { - if let Some(h) = internal.bft_blocks.iter().position(|b| b.blake3_hash().0 == child_fat_pointer.points_at_block_hash()) { + if let Some(h) = internal + .bft_blocks + .iter() + .position(|b| b.blake3_hash().0 == child_fat_pointer.points_at_block_hash()) + { h } else { return false; @@ -239,11 +251,16 @@ async fn block_height_hash_from_hash( } } -async fn block_from_hash( // @Phillip +async fn block_from_hash( + // @Phillip call: &TFLServiceCalls, hash: BlockHash, ) -> Option> { - if let Ok(StateResponse::Block(Some(block))) = (call.state)(StateRequest::Block(zebra_state::HashOrHeight::Hash(hash.into()))).await { + if let Ok(StateResponse::Block(Some(block))) = (call.state)(StateRequest::Block( + zebra_state::HashOrHeight::Hash(hash.into()), + )) + .await + { let check_hash = block.as_ref().hash(); assert_eq!(hash, check_hash); Some(block) @@ -252,12 +269,16 @@ async fn block_from_hash( // @Phillip } } -async fn is_block_known( // @Phillip +async fn is_block_known( + // @Phillip call: &TFLServiceCalls, hash: BlockHash, ) -> bool { - if let Ok(StateResponse::KnownBlock(Some(known_block))) = (call.state)(StateRequest::KnownBlock(hash.into())).await { - known_block.location == zebra_state::KnownBlockLocation::BestChain || known_block.location == zebra_state::KnownBlockLocation::SideChain + if let Ok(StateResponse::KnownBlock(Some(known_block))) = + (call.state)(StateRequest::KnownBlock(hash.into())).await + { + known_block.location == zebra_state::KnownBlockLocation::BestChain + || known_block.location == zebra_state::KnownBlockLocation::SideChain } else { false } @@ -465,7 +486,13 @@ async fn propose_new_bft_block(tfl_handle: &TFLServiceHandle) -> Option { if let Some(staking_action) = staking_action { let txid = tx.unmined_id().mined_id(); - info!("got staking action in txid: {}", StakingAction::str_from_addr(txid.0)); - cmd_c += update_roster_for_cmd(roster, txid.0, validators_keys_to_names, &staking_action); + info!( + "got staking action in txid: {}", + StakingAction::str_from_addr(txid.0) + ); + cmd_c += update_roster_for_cmd( + roster, + txid.0, + validators_keys_to_names, + &staking_action, + ); } }; } @@ -1158,7 +1200,10 @@ fn update_roster_for_block(internal: &mut TFLServiceInternal, block: &Block) -> cmd_c } -async fn tfl_service_main_loop(internal_handle: TFLServiceHandle, global_seed: [u8; 32]) -> Result<(), String> { +async fn tfl_service_main_loop( + internal_handle: TFLServiceHandle, + global_seed: [u8; 32], +) -> Result<(), String> { let call = internal_handle.call.clone(); let config = internal_handle.config.clone(); let params = &PROTOTYPE_PARAMETERS; @@ -1339,7 +1384,8 @@ async fn tfl_service_main_loop(internal_handle: TFLServiceHandle, global_seed: [ panic!(); }) })), - tenderlink::ClosureToGetPow(Arc::new(move |hash| { // @Phillip + tenderlink::ClosureToGetPow(Arc::new(move |hash| { + // @Phillip let tfl_handle5 = tfl_handle5.clone(); Box::pin(async move { if let Some(block) = block_from_hash(&tfl_handle5.call, hash.into()).await { @@ -1357,7 +1403,8 @@ async fn tfl_service_main_loop(internal_handle: TFLServiceHandle, global_seed: [ } }) })), - tenderlink::ClosureToParsePow(Arc::new(move |hash, bytes| { // @Phillip + tenderlink::ClosureToParsePow(Arc::new(move |hash, bytes| { + // @Phillip Box::pin(async move { let mut slice = &bytes[..]; // let slice_ref = &mut slice; @@ -1375,13 +1422,13 @@ async fn tfl_service_main_loop(internal_handle: TFLServiceHandle, global_seed: [ } }) })), - tenderlink::ClosureIsPoWInChain(Arc::new(move |hash| { // @Phillip + tenderlink::ClosureIsPoWInChain(Arc::new(move |hash| { + // @Phillip let tfl_handle6 = tfl_handle6.clone(); - Box::pin(async move { - is_block_known(&tfl_handle6.call, BlockHash(hash)).await - }) + Box::pin(async move { is_block_known(&tfl_handle6.call, BlockHash(hash)).await }) })), - tenderlink::ClosureToPushPow(Arc::new(move |block| { // @Phillip + tenderlink::ClosureToPushPow(Arc::new(move |block| { + // @Phillip let tfl_handle7 = tfl_handle7.clone(); Box::pin(async move { (tfl_handle7.call.force_feed_pow)(block.clone()).await; @@ -1576,7 +1623,11 @@ async fn tfl_service_incoming_request( internal .validators_at_current_height .iter() - .map(|v| RosterMember{ pub_key:<[u8; 32]>::from(v.public_key), voting_power: v.voting_power, txids:v.txids.clone() }) + .map(|v| RosterMember { + pub_key: <[u8; 32]>::from(v.public_key), + voting_power: v.voting_power, + txids: v.txids.clone(), + }) .collect() })), @@ -1914,7 +1965,12 @@ impl MalValidator { } pub fn push_txid(&mut self, txid: StakeTxId) { - if self.txids.iter().find(|cmp| cmp.txid == txid.txid).is_some() { + if self + .txids + .iter() + .find(|cmp| cmp.txid == txid.txid) + .is_some() + { return; } self.voting_power += txid.zats; diff --git a/zebra-gui/src/ui.rs b/zebra-gui/src/ui.rs index 6bcdace0..dcdb3e88 100644 --- a/zebra-gui/src/ui.rs +++ b/zebra-gui/src/ui.rs @@ -1,14 +1,14 @@ #![allow(warnings)] +use clay::layout::{Alignment, LayoutAlignmentX, LayoutAlignmentY}; +use clay::render_commands::RenderCommandConfig; +use clay::render_commands::RenderCommandConfig::{Rectangle, ScissorEnd, ScissorStart}; +use clay::{Clay, Declaration}; +use clay_layout as clay; +use std::hash::Hash; use std::net::Shutdown; use std::thread::current; -use std::{hash::Hash}; use winit::{event::MouseButton, keyboard::KeyCode}; -use clay_layout as clay; -use clay::{Clay, Declaration}; -use clay::render_commands::RenderCommandConfig; -use clay::render_commands::RenderCommandConfig::{Rectangle, ScissorStart, ScissorEnd}; -use clay::layout::{Alignment, LayoutAlignmentX, LayoutAlignmentY}; //use clay::*; // @Temporary use wallet::str_from_ctaz; @@ -24,12 +24,11 @@ pub fn magic<'a, 'b, T>(mut_ref: &'a mut T) -> &'b mut T { pub struct UiData { pub per_frame_strs: Vec, - pub send_address: String, + pub send_address: String, pub stake_address: String, - pub recv_address: String, + pub recv_address: String, } - #[macro_export] macro_rules! frame_strf { ($data:expr, $($arg:tt)*) => { @@ -62,7 +61,10 @@ pub fn dbg_ui(ui: &mut Context, is_rendering: bool) -> bool { if ui.pixel_inspector_primed { if ui.input().mouse_pressed(MouseButton::Left) { unsafe { - *ui.draw().debug_pixel_inspector = Some((ui.input().mouse_pos().0.clamp(0, ui.draw().window_width) as usize, ui.input().mouse_pos().1.clamp(0, ui.draw().window_height) as usize)); + *ui.draw().debug_pixel_inspector = Some(( + ui.input().mouse_pos().0.clamp(0, ui.draw().window_width) as usize, + ui.input().mouse_pos().1.clamp(0, ui.draw().window_height) as usize, + )); } ui.pixel_inspector_primed = false; } @@ -70,66 +72,188 @@ pub fn dbg_ui(ui: &mut Context, is_rendering: bool) -> bool { if is_rendering { if ui.pixel_inspector_primed { - ui.draw().text_line(FontKind::Mono, 0.0, 0.0, 16.0, "Pixel Inspector is Primed! Click to select pixel.", 0xff_00ff00); + ui.draw().text_line( + FontKind::Mono, + 0.0, + 0.0, + 16.0, + "Pixel Inspector is Primed! Click to select pixel.", + 0xff_00ff00, + ); } if let Some((x, y)) = unsafe { *ui.draw().debug_pixel_inspector } { - let x = x as isize; let y = y as isize; + let x = x as isize; + let y = y as isize; let mut draw_x = 0; let mut draw_y = 0; - if x < ui.draw().window_width/2 { draw_x = ui.draw().window_width - 256 }; - if y < ui.draw().window_height/2 { draw_y = ui.draw().window_height - 256 }; + if x < ui.draw().window_width / 2 { + draw_x = ui.draw().window_width - 256 + }; + if y < ui.draw().window_height / 2 { + draw_y = ui.draw().window_height - 256 + }; let color = unsafe { *ui.draw().debug_pixel_inspector_last_color }; - ui.draw().rectangle(draw_x as f32, draw_y as f32, draw_x as f32 + 256.0, draw_y as f32 + 256.0, 0xff_000000 | color); - ui.draw().text_line(FontKind::Mono, draw_x as f32, draw_y as f32, 12.0, &format!("({},{}) = {:X}", x, y, color), 0xff_000000 | (color ^ u32::MAX)); + ui.draw().rectangle( + draw_x as f32, + draw_y as f32, + draw_x as f32 + 256.0, + draw_y as f32 + 256.0, + 0xff_000000 | color, + ); + ui.draw().text_line( + FontKind::Mono, + draw_x as f32, + draw_y as f32, + 12.0, + &format!("({},{}) = {:X}", x, y, color), + 0xff_000000 | (color ^ u32::MAX), + ); } } return false; } -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum Direction { #[default] LeftToRight, TopToBottom } -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum Floating { #[default] None, Parent, Root(f32, f32) } -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum ClipMode { #[default] None, Clip, Scroll(f32, f32) } -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum AlignX { #[default] Left, Right, Center } -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum AlignY { #[default] Top, Bottom, Center } -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct Align { x: AlignX, y: AlignY } -#[derive(Debug, Copy, Clone, PartialEq)] pub enum Sizing { Fit(f32, f32), Grow(f32, f32), Fixed(f32), Percent(f32) } -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct Id { id: u32, offset: u32, base_id: u32, len: usize, chars: *const u8 } -impl Default for Sizing { fn default() -> Self { Self::Fit(0.0, f32::MAX) } } +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub enum Direction { + #[default] + LeftToRight, + TopToBottom, +} +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub enum Floating { + #[default] + None, + Parent, + Root(f32, f32), +} +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub enum ClipMode { + #[default] + None, + Clip, + Scroll(f32, f32), +} +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub enum AlignX { + #[default] + Left, + Right, + Center, +} +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub enum AlignY { + #[default] + Top, + Bottom, + Center, +} +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct Align { + x: AlignX, + y: AlignY, +} +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum Sizing { + Fit(f32, f32), + Grow(f32, f32), + Fixed(f32), + Percent(f32), +} +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct Id { + id: u32, + offset: u32, + base_id: u32, + len: usize, + chars: *const u8, +} +impl Default for Sizing { + fn default() -> Self { + Self::Fit(0.0, f32::MAX) + } +} impl Align { - pub const TopLeft: Self = Self { y: AlignY::Top, x: AlignX::Left }; - pub const Top: Self = Self { y: AlignY::Top, x: AlignX::Center }; - pub const TopRight: Self = Self { y: AlignY::Top, x: AlignX::Right }; - pub const Left: Self = Self { y: AlignY::Center, x: AlignX::Left }; - pub const Center: Self = Self { y: AlignY::Center, x: AlignX::Center }; - pub const Right: Self = Self { y: AlignY::Center, x: AlignX::Right }; - pub const BottomLeft: Self = Self { y: AlignY::Bottom, x: AlignX::Left }; - pub const Bottom: Self = Self { y: AlignY::Bottom, x: AlignX::Center }; - pub const BottomRight: Self = Self { y: AlignY::Bottom, x: AlignX::Right }; + pub const TopLeft: Self = Self { + y: AlignY::Top, + x: AlignX::Left, + }; + pub const Top: Self = Self { + y: AlignY::Top, + x: AlignX::Center, + }; + pub const TopRight: Self = Self { + y: AlignY::Top, + x: AlignX::Right, + }; + pub const Left: Self = Self { + y: AlignY::Center, + x: AlignX::Left, + }; + pub const Center: Self = Self { + y: AlignY::Center, + x: AlignX::Center, + }; + pub const Right: Self = Self { + y: AlignY::Center, + x: AlignX::Right, + }; + pub const BottomLeft: Self = Self { + y: AlignY::Bottom, + x: AlignX::Left, + }; + pub const Bottom: Self = Self { + y: AlignY::Bottom, + x: AlignX::Center, + }; + pub const BottomRight: Self = Self { + y: AlignY::Bottom, + x: AlignX::Right, + }; } // why can we not `use` these? namaste -pub const TopLeft: Align = Align::TopLeft; -pub const Top: Align = Align::Top; -pub const TopRight: Align = Align::TopRight; -pub const Left: Align = Align::Left; -pub const Center: Align = Align::Center; -pub const Right: Align = Align::Right; -pub const BottomLeft: Align = Align::BottomLeft; -pub const Bottom: Align = Align::Bottom; +pub const TopLeft: Align = Align::TopLeft; +pub const Top: Align = Align::Top; +pub const TopRight: Align = Align::TopRight; +pub const Left: Align = Align::Left; +pub const Center: Align = Align::Center; +pub const Right: Align = Align::Right; +pub const BottomLeft: Align = Align::BottomLeft; +pub const Bottom: Align = Align::Bottom; pub const BottomRight: Align = Align::BottomRight; -#[macro_export] macro_rules! fit { - ($min:expr, $max:expr) => { Sizing::Fit($min, $max) }; - ($min:expr) => { fit!($min, f32::MAX) }; - () => { fit!(0.0) }; +#[macro_export] +macro_rules! fit { + ($min:expr, $max:expr) => { + Sizing::Fit($min, $max) + }; + ($min:expr) => { + fit!($min, f32::MAX) + }; + () => { + fit!(0.0) + }; } -#[macro_export] macro_rules! grow { - ($min:expr, $max:expr) => { Sizing::Grow($min, $max) }; - ($min:expr) => { grow!($min, f32::MAX) }; - () => { grow!(0.0) }; +#[macro_export] +macro_rules! grow { + ($min:expr, $max:expr) => { + Sizing::Grow($min, $max) + }; + ($min:expr) => { + grow!($min, f32::MAX) + }; + () => { + grow!(0.0) + }; +} +#[macro_export] +macro_rules! fixed { + ($val:expr) => { + Sizing::Fixed($val) + }; } -#[macro_export] macro_rules! fixed { ($val:expr) => { Sizing::Fixed($val) }; } -#[macro_export] macro_rules! percent { +#[macro_export] +macro_rules! percent { ($percent:expr) => {{ const _: () = assert!( $percent >= 0.0 && $percent <= 1.0, @@ -142,7 +266,13 @@ pub const BottomRight: Align = Align::BottomRight; use ClipMode::{Clip, Scroll}; use Direction::*; -pub const Id: Id = Id { id: 0, offset: 0, base_id: 0, len: 0, chars: std::ptr::null() }; +pub const Id: Id = Id { + id: 0, + offset: 0, + base_id: 0, + len: 0, + chars: std::ptr::null(), +}; #[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct Decl { @@ -155,23 +285,23 @@ pub struct Decl { clip: ClipMode, child_gap: f32, align: Align, - width: Sizing, + width: Sizing, height: Sizing, } // Ease-of-use constant for the builder pattern thing, so you can write Decl{..Decl} to get a default Decl. // I can't do #[derive_const(Default)] because that's only on Rust nightly. And it would probably be complex anyway. pub const Decl: Decl = Decl { - id: Id, + id: Id, direction: Direction::LeftToRight, - floating: Floating::None, - colour: (0, 0, 0, 0), - radius: (0.0, 0.0, 0.0, 0.0), - padding: (0.0, 0.0, 0.0, 0.0), - clip: ClipMode::None, + floating: Floating::None, + colour: (0, 0, 0, 0), + radius: (0.0, 0.0, 0.0, 0.0), + padding: (0.0, 0.0, 0.0, 0.0), + clip: ClipMode::None, child_gap: 0.0, - align: TopLeft, - width: Sizing::Fit(0.0, f32::MAX), - height: Sizing::Fit(0.0, f32::MAX), + align: TopLeft, + width: Sizing::Fit(0.0, f32::MAX), + height: Sizing::Fit(0.0, f32::MAX), }; #[derive(Debug, Default, Copy, Clone, PartialEq)] @@ -190,8 +320,6 @@ pub const TextDecl: TextDecl = TextDecl { break_word: false, }; - - impl Id { pub const VIZ_GUI: Self = Self { id: 1, ..Id }; fn clay(&self) -> clay::id::Id { @@ -203,9 +331,9 @@ impl Id { stringId: clay::Clay_String { isStaticallyAllocated: false, length: self.len as i32, - chars: self.chars as *const i8 - } - } + chars: self.chars as *const i8, + }, + }, } } } @@ -216,34 +344,74 @@ pub fn id(label: &str) -> Id { offset: id.offset, base_id: id.baseId, len: id.stringId.length as usize, - chars: id.stringId.chars as *const u8 + chars: id.stringId.chars as *const u8, } } pub fn id_index(label: &str, index: u32) -> Id { - let id = unsafe { clay::Clay__HashString(label.into(), index, clay::Clay__GetParentElementId()) }; + let id = + unsafe { clay::Clay__HashString(label.into(), index, clay::Clay__GetParentElementId()) }; Id { id: id.id, offset: id.offset, base_id: id.baseId, len: id.stringId.length as usize, - chars: id.stringId.chars as *const u8 + chars: id.stringId.chars as *const u8, } } -#[derive(Default)] pub struct Element { decl: Decl } -pub fn elem() -> Element { unsafe { clay::Clay__OpenElement(); } Element::default() } -impl Drop for Element { fn drop(&mut self) { unsafe { clay::Clay__CloseElement(); } } } +#[derive(Default)] +pub struct Element { + decl: Decl, +} +pub fn elem() -> Element { + unsafe { + clay::Clay__OpenElement(); + } + Element::default() +} +impl Drop for Element { + fn drop(&mut self) { + unsafe { + clay::Clay__CloseElement(); + } + } +} -pub const Clay_ElementId_ZERO: clay::Clay_ElementId = clay::Clay_ElementId { id: 0, offset: 0, baseId: 0, stringId: clay::Clay_String { isStaticallyAllocated: false, length: 0, chars: std::ptr::null() } }; -pub const Clay_SizingMinMax_ZERO: clay::Clay_SizingMinMax = clay::Clay_SizingMinMax { min: 0f32, max: f32::MAX }; +pub const Clay_ElementId_ZERO: clay::Clay_ElementId = clay::Clay_ElementId { + id: 0, + offset: 0, + baseId: 0, + stringId: clay::Clay_String { + isStaticallyAllocated: false, + length: 0, + chars: std::ptr::null(), + }, +}; +pub const Clay_SizingMinMax_ZERO: clay::Clay_SizingMinMax = clay::Clay_SizingMinMax { + min: 0f32, + max: f32::MAX, +}; pub const Clay_SizingAxis_ZERO: clay::Clay_SizingAxis = clay::Clay_SizingAxis { - size: clay::Clay_SizingAxis__bindgen_ty_1 { minMax: Clay_SizingMinMax_ZERO }, - type_: clay::Clay__SizingType_CLAY__SIZING_TYPE_FIT + size: clay::Clay_SizingAxis__bindgen_ty_1 { + minMax: Clay_SizingMinMax_ZERO, + }, + type_: clay::Clay__SizingType_CLAY__SIZING_TYPE_FIT, +}; +pub const Clay_Sizing_ZERO: clay::Clay_Sizing = clay::Clay_Sizing { + width: Clay_SizingAxis_ZERO, + height: Clay_SizingAxis_ZERO, +}; +pub const Clay_Padding_ZERO: clay::Clay_Padding = clay::Clay_Padding { + left: 0, + right: 0, + top: 0, + bottom: 0, +}; +pub const Clay_ChildAlignment_ZERO: clay::Clay_ChildAlignment = clay::Clay_ChildAlignment { + x: 0 as _, + y: 0 as _, }; -pub const Clay_Sizing_ZERO: clay::Clay_Sizing = clay::Clay_Sizing { width: Clay_SizingAxis_ZERO, height: Clay_SizingAxis_ZERO }; -pub const Clay_Padding_ZERO: clay::Clay_Padding = clay::Clay_Padding { left: 0, right: 0, top: 0, bottom: 0 }; -pub const Clay_ChildAlignment_ZERO: clay::Clay_ChildAlignment = clay::Clay_ChildAlignment { x: 0 as _, y: 0 as _ }; pub const Clay_LayoutConfig_ZERO: clay::Clay_LayoutConfig = clay::Clay_LayoutConfig { sizing: Clay_Sizing_ZERO, padding: Clay_Padding_ZERO, @@ -251,45 +419,73 @@ pub const Clay_LayoutConfig_ZERO: clay::Clay_LayoutConfig = clay::Clay_LayoutCon childAlignment: Clay_ChildAlignment_ZERO, layoutDirection: clay::Clay_LayoutDirection_CLAY_LEFT_TO_RIGHT, }; -pub const Clay_Color_ZERO: clay::Clay_Color = clay::Clay_Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }; -pub const Clay_CornerRadius_ZERO: clay::Clay_CornerRadius = clay::Clay_CornerRadius { topLeft: 0f32, topRight: 0f32, bottomLeft: 0f32, bottomRight: 0f32 }; -pub const Clay_ElementDeclaration_ZERO: clay::Clay_ElementDeclaration = clay::Clay_ElementDeclaration { - id: Clay_ElementId_ZERO, - layout: Clay_LayoutConfig_ZERO, - backgroundColor: Clay_Color_ZERO, - cornerRadius: Clay_CornerRadius_ZERO, - aspectRatio: clay::Clay_AspectRatioElementConfig { aspectRatio: 0.0 }, - image: clay::Clay_ImageElementConfig { imageData: std::ptr::null_mut() }, - floating: clay::Clay_FloatingElementConfig { - offset: clay::Clay_Vector2 { x: 0f32, y: 0f32 }, - expand: clay::Clay_Dimensions { width: 0f32, height: 0f32 }, - parentId: 0, - zIndex: 0, - attachPoints: clay::Clay_FloatingAttachPoints { - element: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_LEFT_TOP, - parent: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_LEFT_TOP, - }, - pointerCaptureMode: clay::Clay_PointerCaptureMode_CLAY_POINTER_CAPTURE_MODE_CAPTURE, - attachTo: clay::Clay_FloatingAttachToElement_CLAY_ATTACH_TO_NONE, - clipTo: clay::Clay_FloatingClipToElement_CLAY_CLIP_TO_NONE - }, - custom: clay::Clay_CustomElementConfig { customData: std::ptr::null_mut() }, - clip: clay::Clay_ClipElementConfig { horizontal: false, vertical: false, childOffset: clay::Clay_Vector2 { x: 0f32, y: 0f32 } }, - border: clay::Clay_BorderElementConfig { - color: Clay_Color_ZERO, - width: clay::Clay_BorderWidth { left: 0, right: 0, top: 0, bottom: 0, betweenChildren: 0 } - }, - userData: std::ptr::null_mut(), +pub const Clay_Color_ZERO: clay::Clay_Color = clay::Clay_Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.0, +}; +pub const Clay_CornerRadius_ZERO: clay::Clay_CornerRadius = clay::Clay_CornerRadius { + topLeft: 0f32, + topRight: 0f32, + bottomLeft: 0f32, + bottomRight: 0f32, }; +pub const Clay_ElementDeclaration_ZERO: clay::Clay_ElementDeclaration = + clay::Clay_ElementDeclaration { + id: Clay_ElementId_ZERO, + layout: Clay_LayoutConfig_ZERO, + backgroundColor: Clay_Color_ZERO, + cornerRadius: Clay_CornerRadius_ZERO, + aspectRatio: clay::Clay_AspectRatioElementConfig { aspectRatio: 0.0 }, + image: clay::Clay_ImageElementConfig { + imageData: std::ptr::null_mut(), + }, + floating: clay::Clay_FloatingElementConfig { + offset: clay::Clay_Vector2 { x: 0f32, y: 0f32 }, + expand: clay::Clay_Dimensions { + width: 0f32, + height: 0f32, + }, + parentId: 0, + zIndex: 0, + attachPoints: clay::Clay_FloatingAttachPoints { + element: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_LEFT_TOP, + parent: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_LEFT_TOP, + }, + pointerCaptureMode: clay::Clay_PointerCaptureMode_CLAY_POINTER_CAPTURE_MODE_CAPTURE, + attachTo: clay::Clay_FloatingAttachToElement_CLAY_ATTACH_TO_NONE, + clipTo: clay::Clay_FloatingClipToElement_CLAY_CLIP_TO_NONE, + }, + custom: clay::Clay_CustomElementConfig { + customData: std::ptr::null_mut(), + }, + clip: clay::Clay_ClipElementConfig { + horizontal: false, + vertical: false, + childOffset: clay::Clay_Vector2 { x: 0f32, y: 0f32 }, + }, + border: clay::Clay_BorderElementConfig { + color: Clay_Color_ZERO, + width: clay::Clay_BorderWidth { + left: 0, + right: 0, + top: 0, + bottom: 0, + betweenChildren: 0, + }, + }, + userData: std::ptr::null_mut(), + }; impl Element { fn decl(&mut self, item: Decl) -> &mut Self { fn sizing(sizing: Sizing) -> clay::layout::Sizing { match sizing { - Sizing::Fit(min, max) => { clay::layout::Sizing::Fit(min, max) } - Sizing::Grow(min, max) => { clay::layout::Sizing::Grow(min, max) } - Sizing::Fixed(x) => { clay::layout::Sizing::Fixed(x) } - Sizing::Percent(p) => { clay::layout::Sizing::Percent(p) } + Sizing::Fit(min, max) => clay::layout::Sizing::Fit(min, max), + Sizing::Grow(min, max) => clay::layout::Sizing::Grow(min, max), + Sizing::Fixed(x) => clay::layout::Sizing::Fixed(x), + Sizing::Percent(p) => clay::layout::Sizing::Percent(p), } } @@ -298,10 +494,13 @@ impl Element { r: item.colour.0 as f32, g: item.colour.1 as f32, b: item.colour.2 as f32, - a: item.colour.3 as f32 + a: item.colour.3 as f32, }; decl.id = item.id.clay().id; - let clipping = match item.clip { ClipMode::None => false, _ => true }; + let clipping = match item.clip { + ClipMode::None => false, + _ => true, + }; decl.clip = clay::Clay_ClipElementConfig { horizontal: clipping, vertical: clipping, @@ -309,47 +508,53 @@ impl Element { ClipMode::None => clay::Clay_Vector2 { x: 0.0, y: 0.0 }, ClipMode::Clip => clay::Clay_Vector2 { x: 0.0, y: 0.0 }, ClipMode::Scroll(x, y) => clay::Clay_Vector2 { x, y }, - } + }, }; match item.floating { Floating::Parent => { decl.floating.attachTo = clay::Clay_FloatingAttachToElement_CLAY_ATTACH_TO_PARENT; - decl.floating.clipTo = clay::Clay_FloatingClipToElement_CLAY_CLIP_TO_ATTACHED_PARENT; + decl.floating.clipTo = + clay::Clay_FloatingClipToElement_CLAY_CLIP_TO_ATTACHED_PARENT; decl.floating.attachPoints = clay::Clay_FloatingAttachPoints { element: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_CENTER_CENTER, - parent: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_CENTER_CENTER, + parent: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_CENTER_CENTER, }; - }, + } Floating::Root(x, y) => { decl.floating.attachTo = clay::Clay_FloatingAttachToElement_CLAY_ATTACH_TO_ROOT; decl.floating.offset.x = x; decl.floating.offset.y = y; decl.floating.attachPoints = clay::Clay_FloatingAttachPoints { element: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_LEFT_TOP, - parent: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_LEFT_TOP, + parent: clay::Clay_FloatingAttachPointType_CLAY_ATTACH_POINT_LEFT_TOP, }; - }, - _ => {}, + } + _ => {} } - decl.layout.sizing.width = clay::Clay_SizingAxis::from(sizing(item.width)); + decl.layout.sizing.width = clay::Clay_SizingAxis::from(sizing(item.width)); decl.layout.sizing.height = clay::Clay_SizingAxis::from(sizing(item.height)); decl.layout.padding = clay::Clay_Padding::from(clay::Clay_Padding { - left: item.padding.0 as u16, - right: item.padding.1 as u16, - top: item.padding.2 as u16, + left: item.padding.0 as u16, + right: item.padding.1 as u16, + top: item.padding.2 as u16, bottom: item.padding.3 as u16, }); decl.layout.childGap = item.child_gap as u16; - decl.layout.childAlignment = clay::Clay_ChildAlignment { x: item.align.x as _, y: item.align.y as _ }; + decl.layout.childAlignment = clay::Clay_ChildAlignment { + x: item.align.x as _, + y: item.align.y as _, + }; decl.layout.layoutDirection = item.direction as _; decl.cornerRadius = clay::Clay_CornerRadius { - topLeft: item.radius.0, - topRight: item.radius.1, - bottomLeft: item.radius.2, - bottomRight: item.radius.3 + topLeft: item.radius.0, + topRight: item.radius.1, + bottomLeft: item.radius.2, + bottomRight: item.radius.3, }; - unsafe { clay::Clay__ConfigureOpenElement(decl); } + unsafe { + clay::Clay__ConfigureOpenElement(decl); + } self.decl = item; self @@ -358,25 +563,33 @@ impl Element { pub const PANE_PERCENT: f32 = 0.27; // @PreventPanesColliding -pub const WHITE: (u8, u8, u8, u8) = (0xff, 0xff, 0xff, 0xff); +pub const WHITE: (u8, u8, u8, u8) = (0xff, 0xff, 0xff, 0xff); // pub const PANE_COL: (u8, u8, u8, u8) = (0x12, 0x12, 0x12, 0xff); // @FigmaScreenshot -pub const PANE_COL: (u8, u8, u8, u8) = (0x13, 0x13, 0x13, 0xff); // @FigmaScreenshot +pub const PANE_COL: (u8, u8, u8, u8) = (0x13, 0x13, 0x13, 0xff); // @FigmaScreenshot pub const INACTIVE_TAB_COL: (u8, u8, u8, u8) = (0x0f, 0x0f, 0x0f, 0xff); -pub const ACTIVE_TAB_COL: (u8, u8, u8, u8) = PANE_COL; +pub const ACTIVE_TAB_COL: (u8, u8, u8, u8) = PANE_COL; -pub const BUTTON_GREY: (u8, u8, u8, u8) = (0x24, 0x24, 0x24, 0xff); // @FigmaScreenshot -pub const BUTTON_BLUE: (u8, u8, u8, u8) = (0x1a, 0x36, 0x51, 0xff); // @FigmaScreenshot -pub const BUTTON_ORANGE: (u8, u8, u8, u8) = (0x59, 0x41, 0x11, 0xff); // @FigmaScreenshot +pub const BUTTON_GREY: (u8, u8, u8, u8) = (0x24, 0x24, 0x24, 0xff); // @FigmaScreenshot +pub const BUTTON_BLUE: (u8, u8, u8, u8) = (0x1a, 0x36, 0x51, 0xff); // @FigmaScreenshot +pub const BUTTON_ORANGE: (u8, u8, u8, u8) = (0x59, 0x41, 0x11, 0xff); // @FigmaScreenshot pub const MODAL_COL: (u8, u8, u8, u8) = (0x1e, 0x1e, 0x1e, 0xff); // @FigmaScreenshot pub const TRANSACTION_HISTORY_CONTAINER_COL: (u8, u8, u8, u8) = (0x22, 0x22, 0x24, 0xff); // @FigmaScreenshot -pub const fn clay_colour(colour: (u8, u8, u8, u8)) -> clay::Color { clay::Color::rgba(colour.0 as f32, colour.1 as f32, colour.2 as f32, colour.3 as f32) } +pub const fn clay_colour(colour: (u8, u8, u8, u8)) -> clay::Color { + clay::Color::rgba( + colour.0 as f32, + colour.1 as f32, + colour.2 as f32, + colour.3 as f32, + ) +} #[derive(Debug, Default, Copy, Clone, PartialEq)] pub enum Modal { - #[default] None, + #[default] + None, Send, Receive, Stake, @@ -416,9 +629,9 @@ pub fn rgba_to_hsva(r: u8, g: u8, b: u8, a: u8) -> (u8, u8, u8, u8) { } pub fn hsva_to_rgba(h: u8, s: u8, v: u8, a: u8) -> (u8, u8, u8, u8) { - let hf = (h as f32 / 255.0) * 6.0; // 0..6 - let sf = s as f32 / 255.0; // 0..1 - let vf = v as f32 / 255.0; // 0..1 + let hf = (h as f32 / 255.0) * 6.0; // 0..6 + let sf = s as f32 / 255.0; // 0..1 + let vf = v as f32 / 255.0; // 0..1 let c = vf * sf; let x = c * (1.0 - ((hf % 2.0) - 1.0).abs()); @@ -441,34 +654,70 @@ pub fn hsva_to_rgba(h: u8, s: u8, v: u8, a: u8) -> (u8, u8, u8, u8) { } pub trait HSVA_RGBA { - #[inline(always)] fn hsva(self) -> Self; - #[inline(always)] fn rgba(self) -> Self; + #[inline(always)] + fn hsva(self) -> Self; + #[inline(always)] + fn rgba(self) -> Self; } impl HSVA_RGBA for (u8, u8, u8, u8) { - #[inline(always)] fn hsva(self) -> Self { rgba_to_hsva(self.0, self.1, self.2, self.3) } - #[inline(always)] fn rgba(self) -> Self { hsva_to_rgba(self.0, self.1, self.2, self.3) } + #[inline(always)] + fn hsva(self) -> Self { + rgba_to_hsva(self.0, self.1, self.2, self.3) + } + #[inline(always)] + fn rgba(self) -> Self { + hsva_to_rgba(self.0, self.1, self.2, self.3) + } } impl Context { - pub fn new() -> Context { Context { scale: 1f32, zoom: 1f32, dpi_scale: 1f32, ..Default::default() } } - pub fn draw(&self) -> &DrawCtx { unsafe { &*self.draw } } - pub fn input(&self) -> &InputCtx { unsafe { &*self.input } } - pub fn clay(&self) -> &mut Clay { unsafe { &mut *self.clay } } + pub fn new() -> Context { + Context { + scale: 1f32, + zoom: 1f32, + dpi_scale: 1f32, + ..Default::default() + } + } + pub fn draw(&self) -> &DrawCtx { + unsafe { &*self.draw } + } + pub fn input(&self) -> &InputCtx { + unsafe { &*self.input } + } + pub fn clay(&self) -> &mut Clay { + unsafe { &mut *self.clay } + } - pub fn scale(&self, size: f32) -> f32 { (size * self.scale).floor() } - pub fn scale32(&self, size: f32) -> u32 { self.scale(size) as u32 } - pub fn scale16(&self, size: f32) -> u16 { self.scale(size) as u16 } + pub fn scale(&self, size: f32) -> f32 { + (size * self.scale).floor() + } + pub fn scale32(&self, size: f32) -> u32 { + self.scale(size) as u32 + } + pub fn scale16(&self, size: f32) -> u16 { + self.scale(size) as u16 + } - pub fn hovered(&self, id: Id) -> bool { unsafe { clay::Clay_PointerOver(id.clay().id) } } + pub fn hovered(&self, id: Id) -> bool { + unsafe { clay::Clay_PointerOver(id.clay().id) } + } - pub fn button_ex(&mut self, act_on_press: bool, colour: (u8, u8, u8, u8), id: Id, enabled: bool, pointer_on_hover: bool) -> (bool, (u8, u8, u8, u8), (u8, u8, u8, u8)) { - let mouse_held = self.input().mouse_held(winit::event::MouseButton::Left); - let mouse_pressed = self.input().mouse_pressed(winit::event::MouseButton::Left); + pub fn button_ex( + &mut self, + act_on_press: bool, + colour: (u8, u8, u8, u8), + id: Id, + enabled: bool, + pointer_on_hover: bool, + ) -> (bool, (u8, u8, u8, u8), (u8, u8, u8, u8)) { + let mouse_held = self.input().mouse_held(winit::event::MouseButton::Left); + let mouse_pressed = self.input().mouse_pressed(winit::event::MouseButton::Left); let mouse_released = self.input().mouse_released(winit::event::MouseButton::Left); - let hover = self.hovered(id); - let down = hover && mouse_held; - let pressed = hover && mouse_pressed; + let hover = self.hovered(id); + let down = hover && mouse_held; + let pressed = hover && mouse_pressed; let released = hover && mouse_released; if pressed { self.clicked_id = id; @@ -478,11 +727,8 @@ impl Context { self.cursor = winit::window::Cursor::Icon(winit::window::CursorIcon::Pointer); } - let activated = enabled && (self.clicked_id == id) && if act_on_press { - pressed - } else { - released - }; + let activated = + enabled && (self.clicked_id == id) && if act_on_press { pressed } else { released }; let mut hsva = colour.hsva(); if !enabled { @@ -513,7 +759,9 @@ impl Context { (activated, colour, text_colour) } - pub fn button(&mut self, id: Id) -> (bool, (u8, u8, u8, u8), (u8, u8, u8, u8)) { return self.button_ex(true, BUTTON_GREY, id, true, false); } + pub fn button(&mut self, id: Id) -> (bool, (u8, u8, u8, u8), (u8, u8, u8, u8)) { + return self.button_ex(true, BUTTON_GREY, id, true, false); + } pub fn text(&self, label: &str, decl: TextDecl) { let config = clay::text::TextConfig::new() @@ -521,8 +769,8 @@ impl Context { .font_size(decl.h as u16) .color(clay_colour(decl.colour)) .alignment(match decl.align { - AlignX::Left => clay::text::TextAlignment::Left, - AlignX::Right => clay::text::TextAlignment::Right, + AlignX::Left => clay::text::TextAlignment::Left, + AlignX::Right => clay::text::TextAlignment::Right, AlignX::Center => clay::text::TextAlignment::Center, }) // .wrap_mode(if decl.break_word { @@ -534,12 +782,14 @@ impl Context { unsafe { clay::Clay__OpenTextElement(label.into(), config.into()) }; } - pub fn tab_ex(&mut self, - radius: (f32, f32, f32, f32), - padding: (f32, f32, f32, f32), - tab_id: &mut Id, - id: Id, - label: &str) -> Id { + pub fn tab_ex( + &mut self, + radius: (f32, f32, f32, f32), + padding: (f32, f32, f32, f32), + tab_id: &mut Id, + id: Id, + label: &str, + ) -> Id { let tab_text_h = self.scale(18.0); let radius = (radius.0, radius.1, 0.0, 0.0); @@ -551,82 +801,236 @@ impl Context { if let _ = elem().decl(Decl { id, - radius, padding, - colour: if *tab_id == id { ACTIVE_TAB_COL } else { INACTIVE_TAB_COL }, + radius, + padding, + colour: if *tab_id == id { + ACTIVE_TAB_COL + } else { + INACTIVE_TAB_COL + }, width: grow!(), height: grow!(), align: Center, ..Decl }) { - self.text(label, TextDecl { h: tab_text_h, align: AlignX::Center, ..TextDecl }); + self.text( + label, + TextDecl { + h: tab_text_h, + align: AlignX::Center, + ..TextDecl + }, + ); } id } - pub fn tab(&mut self, - radius: (f32, f32, f32, f32), - padding: (f32, f32, f32, f32), - tab_id: &mut Id, - label: &str) -> Id { + pub fn tab( + &mut self, + radius: (f32, f32, f32, f32), + padding: (f32, f32, f32, f32), + tab_id: &mut Id, + label: &str, + ) -> Id { let id = id(label); self.tab_ex(radius, padding, tab_id, id, label) } } -pub trait Dup2: Copy { fn dup2(self) -> (Self, Self); } -impl Dup2 for T { fn dup2(self) -> (Self, Self) { (self, self) } } -pub trait Dup3: Copy { fn dup3(self) -> (Self, Self, Self); } -impl Dup3 for T { fn dup3(self) -> (Self, Self, Self) { (self, self, self) } } -pub trait Dup4: Copy { fn dup4(self) -> (Self, Self, Self, Self); } -impl Dup4 for T { fn dup4(self) -> (Self, Self, Self, Self) { (self, self, self, self) } } +pub trait Dup2: Copy { + fn dup2(self) -> (Self, Self); +} +impl Dup2 for T { + fn dup2(self) -> (Self, Self) { + (self, self) + } +} +pub trait Dup3: Copy { + fn dup3(self) -> (Self, Self, Self); +} +impl Dup3 for T { + fn dup3(self) -> (Self, Self, Self) { + (self, self, self) + } +} +pub trait Dup4: Copy { + fn dup4(self) -> (Self, Self, Self, Self); +} +impl Dup4 for T { + fn dup4(self) -> (Self, Self, Self, Self) { + (self, self, self, self) + } +} // Implementation of `tuple.mul(scalar)`. Helper "AsF32" trait to get it working. // This would literally be a two-liner, if Rust generics had C++-style SFINAE. -pub trait AsF32 { #[inline(always)] fn to_f32(self) -> f32; #[inline(always)] fn from_f32(x: f32) -> Self; } -impl AsF32 for u8 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for u16 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for u32 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for u64 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for i8 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for i16 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for i32 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for i64 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for f32 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } -impl AsF32 for f64 { #[inline(always)] fn to_f32(self) -> f32 { self as f32 } #[inline(always)] fn from_f32(x: f32) -> Self { x as Self } } - -pub trait Mul { #[inline(always)] fn mul(self, f: f32) -> Self; } -impl Mul for T { #[inline(always)] fn mul(self, f: f32) -> Self { T::from_f32(self.to_f32() * f) } } -impl Mul for (A, B) { #[inline(always)] fn mul(self, f: f32) -> Self { (self.0.mul(f), self.1.mul(f)) } } -impl Mul for (A, B, C) { #[inline(always)] fn mul(self, f: f32) -> Self { (self.0.mul(f), self.1.mul(f), self.2.mul(f)) } } -impl Mul for (A, B, C, D) { #[inline(always)] fn mul(self, f: f32) -> Self { (self.0.mul(f), self.1.mul(f), self.2.mul(f), self.3.mul(f)) } } - -pub fn ui_left_pane(ui: &mut Context, - wallet_state: Arc>, - data: &mut UiData, - viz: &mut VizState, - child_gap: f32, - padding: (f32, f32, f32, f32), - radius: (f32, f32, f32, f32), - tab_id: &mut Id) { - - if ui.modal != Modal::None && let _elem = elem().decl(Decl { - child_gap, - id: id("Modal Container"), - padding: padding.mul(2.0), - radius: (radius.0, 0.0, radius.2, 0.0), - floating: Floating::Parent, - colour: (0, 0, 0, 0xC0), - align: Center, - width: grow!(), - height: grow!(), - ..Decl - }) { +pub trait AsF32 { + #[inline(always)] + fn to_f32(self) -> f32; + #[inline(always)] + fn from_f32(x: f32) -> Self; +} +impl AsF32 for u8 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for u16 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for u32 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for u64 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for i8 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for i16 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for i32 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for i64 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for f32 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} +impl AsF32 for f64 { + #[inline(always)] + fn to_f32(self) -> f32 { + self as f32 + } + #[inline(always)] + fn from_f32(x: f32) -> Self { + x as Self + } +} + +pub trait Mul { + #[inline(always)] + fn mul(self, f: f32) -> Self; +} +impl Mul for T { + #[inline(always)] + fn mul(self, f: f32) -> Self { + T::from_f32(self.to_f32() * f) + } +} +impl Mul for (A, B) { + #[inline(always)] + fn mul(self, f: f32) -> Self { + (self.0.mul(f), self.1.mul(f)) + } +} +impl Mul for (A, B, C) { + #[inline(always)] + fn mul(self, f: f32) -> Self { + (self.0.mul(f), self.1.mul(f), self.2.mul(f)) + } +} +impl Mul for (A, B, C, D) { + #[inline(always)] + fn mul(self, f: f32) -> Self { + (self.0.mul(f), self.1.mul(f), self.2.mul(f), self.3.mul(f)) + } +} +pub fn ui_left_pane( + ui: &mut Context, + wallet_state: Arc>, + data: &mut UiData, + viz: &mut VizState, + child_gap: f32, + padding: (f32, f32, f32, f32), + radius: (f32, f32, f32, f32), + tab_id: &mut Id, +) { + if ui.modal != Modal::None + && let _elem = elem().decl(Decl { + child_gap, + id: id("Modal Container"), + padding: padding.mul(2.0), + radius: (radius.0, 0.0, radius.2, 0.0), + floating: Floating::Parent, + colour: (0, 0, 0, 0xC0), + align: Center, + width: grow!(), + height: grow!(), + ..Decl + }) + { let container_id = _elem.decl.id; let container_hovered = ui.hovered(container_id); - if container_hovered { ui.capture = true; } + if container_hovered { + ui.capture = true; + } let mut height = grow!(ui.scale(192.0), ui.scale(384.0)); if ui.modal == Modal::Stake { @@ -634,47 +1038,74 @@ pub fn ui_left_pane(ui: &mut Context, } if let _elem = elem().decl(Decl { - child_gap, radius, + child_gap, + radius, id: id("Modal Contents"), padding: padding.mul(2.0), colour: MODAL_COL, - width: grow!(ui.scale(192.0), ui.scale(384.0)), + width: grow!(ui.scale(192.0), ui.scale(384.0)), height: height, align: Top, direction: TopToBottom, ..Decl }) { - let contents_id = _elem.decl.id; let contents_hovered = ui.hovered(contents_id); - if container_hovered { ui.capture = true; } + if container_hovered { + ui.capture = true; + } let text_h = ui.scale(24.0); let title_bar = |ui: &mut Context, closeable, title, title_bar_id| { if let _ = elem().decl(Decl { id: title_bar_id, child_gap, - width: grow!(), + width: grow!(), height: fit!(), align: Center, direction: LeftToRight, ..Decl }) { - if let _ = elem().decl(Decl { width: grow!(), align: Left, ..Decl }) {} - if let _ = elem().decl(Decl { width: grow!(), align: Center, ..Decl }) { - ui.text(title, TextDecl { h: text_h, align: AlignX::Center, ..TextDecl }); + if let _ = elem().decl(Decl { + width: grow!(), + align: Left, + ..Decl + }) {} + if let _ = elem().decl(Decl { + width: grow!(), + align: Center, + ..Decl + }) { + ui.text( + title, + TextDecl { + h: text_h, + align: AlignX::Center, + ..TextDecl + }, + ); } - if let _ = elem().decl(Decl { id: id("Title Bar Right Side"), width: grow!(), align: Right, ..Decl }) && closeable { + if let _ = elem().decl(Decl { + id: id("Title Bar Right Side"), + width: grow!(), + align: Right, + ..Decl + }) && closeable + { let id = id("Close This Modal"); - let (clicked, colour, _) = ui.button_ex(false, BUTTON_GREY, id, true, false); + let (clicked, colour, _) = + ui.button_ex(false, BUTTON_GREY, id, true, false); if clicked || ui.input().key_pressed(KeyCode::Escape) { ui.modal = Modal::None; } // Click background to exit -- the code could be placed farther outside but it is here so it can be gated by `closeable` - if ui.hovered(container_id) && !ui.hovered(contents_id) && ui.input().mouse_pressed(winit::event::MouseButton::Left) { + if ui.hovered(container_id) + && !ui.hovered(contents_id) + && ui.input().mouse_pressed(winit::event::MouseButton::Left) + { ui.modal = Modal::None; ui.clicked_id = id; } @@ -683,13 +1114,26 @@ pub fn ui_left_pane(ui: &mut Context, // Button circle if let _ = elem().decl(Decl { - id, colour, radius: radius.dup4(), padding, child_gap, align: Center, - width: fixed!(radius * 2.0), + id, + colour, + radius: radius.dup4(), + padding, + child_gap, + align: Center, + width: fixed!(radius * 2.0), height: fixed!(radius * 2.0), ..Decl }) { let temp_letter_symbol_h = ui.scale(24.0); - ui.text(ICON_CANCEL, TextDecl { font: Icons, h: temp_letter_symbol_h, align: AlignX::Center, ..TextDecl }); + ui.text( + ICON_CANCEL, + TextDecl { + font: Icons, + h: temp_letter_symbol_h, + align: AlignX::Center, + ..TextDecl + }, + ); } } } @@ -702,7 +1146,8 @@ pub fn ui_left_pane(ui: &mut Context, hsva.2 = ((hsva.2 as f32) * 1.25).min(255.0) as u8; hsva.rgba() }; - let (clicked, colour, text_colour) = ui.button_ex(false, colour, id, enabled, false); + let (clicked, colour, text_colour) = + ui.button_ex(false, colour, id, enabled, false); let radius = ui.scale(24.0); if let _ = elem().decl(Decl { id, @@ -711,12 +1156,20 @@ pub fn ui_left_pane(ui: &mut Context, radius: radius.dup4(), align: Align::Center, direction: TopToBottom, - width: fit!(ui.scale(192.0)), + width: fit!(ui.scale(192.0)), height: fit!(radius * 2.0), ..Decl }) { let h = ui.scale(20.0); - ui.text(label, TextDecl { h, colour: text_colour, align: AlignX::Center, ..TextDecl }); + ui.text( + label, + TextDecl { + h, + colour: text_colour, + align: AlignX::Center, + ..TextDecl + }, + ); } clicked @@ -725,65 +1178,101 @@ pub fn ui_left_pane(ui: &mut Context, match ui.modal { Modal::None => {} Modal::Send => { - title_bar(ui, true, "Send", id("Send Title Bar")); + title_bar(ui, true, "Send", id("Send Title Bar")); if let _ = elem().decl(Decl { - child_gap, radius, + child_gap, + radius, id: id("Send Container"), colour: MODAL_COL, - width: grow!(), + width: grow!(), height: grow!(), align: Center, direction: TopToBottom, ..Decl }) { // spacer - if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(4.0)), ..Default::default() }) {} + if let _ = elem().decl(Decl { + width: grow!(), + height: fixed!(ui.scale(4.0)), + ..Default::default() + }) {} let mut send_address = "0000000000000000"; if data.send_address.len() >= 16 { send_address = &data.send_address; } - ui.text(frame_strf!(data, "[{}..{}]", &send_address[..8], &send_address[send_address.len() - 8..]), TextDecl { font: Mono, h: ui.scale(20.0), colour: WHITE, align: AlignX::Center, ..TextDecl }); + ui.text( + frame_strf!( + data, + "[{}..{}]", + &send_address[..8], + &send_address[send_address.len() - 8..] + ), + TextDecl { + font: Mono, + h: ui.scale(20.0), + colour: WHITE, + align: AlignX::Center, + ..TextDecl + }, + ); if button_ex(ui, "Paste Address", true) { data.send_address = ui.input().get_from_clipboard().trim().to_string(); } // spacer - if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(16.0)), ..Default::default() }) {} + if let _ = elem().decl(Decl { + width: grow!(), + height: fixed!(ui.scale(16.0)), + ..Default::default() + }) {} if (wallet_state.lock().unwrap().balance as u64) < ONE_cTAZ / 100 { let colour = (0xff, 0xaf, 0x0e, 0xff); - ui.text("Insufficient funds. Try the faucet!", TextDecl { h: ui.scale(20.0), colour, align: AlignX::Center, ..TextDecl }); + ui.text( + "Insufficient funds. Try the faucet!", + TextDecl { + h: ui.scale(20.0), + colour, + align: AlignX::Center, + ..TextDecl + }, + ); } const ONE_cTAZ: u64 = 100_000_000; if let _ = elem().decl(Decl { - child_gap, radius, + child_gap, + radius, id: id("Send Buttons"), colour: MODAL_COL, - width: grow!(), + width: grow!(), height: grow!(), align: Center, direction: TopToBottom, ..Decl }) { - let ( - balance, - waiting_for_send - ) = { + let (balance, waiting_for_send) = { let wallet_state = wallet_state.lock().unwrap(); - ( - wallet_state.balance, - wallet_state.waiting_for_send, - ) + (wallet_state.balance, wallet_state.waiting_for_send) }; let can = !waiting_for_send && data.send_address.len() != 0; - if button_ex(ui, "1 cTAZ", can && (balance as u64) >= ONE_cTAZ) { wallet_state.lock().unwrap().send_to_address(data.send_address.clone(), ONE_cTAZ); } - if button_ex(ui, "10 cTAZ", can && (balance as u64) >= ONE_cTAZ * 10) { wallet_state.lock().unwrap().send_to_address(data.send_address.clone(), ONE_cTAZ * 10); } + if button_ex(ui, "1 cTAZ", can && (balance as u64) >= ONE_cTAZ) { + wallet_state + .lock() + .unwrap() + .send_to_address(data.send_address.clone(), ONE_cTAZ); + } + if button_ex(ui, "10 cTAZ", can && (balance as u64) >= ONE_cTAZ * 10) { + wallet_state + .lock() + .unwrap() + .send_to_address(data.send_address.clone(), ONE_cTAZ * 10); + } } } } @@ -791,10 +1280,11 @@ pub fn ui_left_pane(ui: &mut Context, title_bar(ui, true, "Receive", id("Receive Title Bar")); if let _ = elem().decl(Decl { - child_gap, radius, + child_gap, + radius, id: id("Receive Container"), colour: MODAL_COL, - width: grow!(), + width: grow!(), height: grow!(), align: Center, direction: TopToBottom, @@ -802,13 +1292,31 @@ pub fn ui_left_pane(ui: &mut Context, }) { let ua = &wallet_state.lock().unwrap().user_recv_ua; if ua.len() != 0 { - ui.text(frame_strf!(data, "[{}..{}]", &ua[..8], &ua[ua.len() - 8..]), TextDecl { font: Mono, h: ui.scale(20.0), colour: WHITE, align: AlignX::Center, ..TextDecl }); + ui.text( + frame_strf!(data, "[{}..{}]", &ua[..8], &ua[ua.len() - 8..]), + TextDecl { + font: Mono, + h: ui.scale(20.0), + colour: WHITE, + align: AlignX::Center, + ..TextDecl + }, + ); if button_ex(ui, "Copy Address", true) { ui.input().send_to_clipboard(&ua); } } else { - ui.text("Loading...", TextDecl { font: Mono, h: ui.scale(20.0), colour: WHITE, align: AlignX::Center, ..TextDecl }); + ui.text( + "Loading...", + TextDecl { + font: Mono, + h: ui.scale(20.0), + colour: WHITE, + align: AlignX::Center, + ..TextDecl + }, + ); } } } @@ -820,21 +1328,48 @@ pub fn ui_left_pane(ui: &mut Context, stake_address = &data.stake_address; } - ui.text(frame_strf!(data, "[{}..{}]", &stake_address[0..8], &stake_address[stake_address.len()-8..]), TextDecl { font: Mono, h: ui.scale(20.0), colour: WHITE, align: AlignX::Center, ..TextDecl }); + ui.text( + frame_strf!( + data, + "[{}..{}]", + &stake_address[0..8], + &stake_address[stake_address.len() - 8..] + ), + TextDecl { + font: Mono, + h: ui.scale(20.0), + colour: WHITE, + align: AlignX::Center, + ..TextDecl + }, + ); if button_ex(ui, "Paste Address", true) { data.stake_address = ui.input().get_from_clipboard().trim().to_string(); } // spacer - if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(16.0)), ..Default::default() }) {} + if let _ = elem().decl(Decl { + width: grow!(), + height: fixed!(ui.scale(16.0)), + ..Default::default() + }) {} if (wallet_state.lock().unwrap().balance as u64) < ONE_cTAZ / 100 { let colour = (0xff, 0xaf, 0x0e, 0xff); - ui.text("Insufficient funds. Try the faucet!", TextDecl { h: ui.scale(20.0), colour, align: AlignX::Center, ..TextDecl }); + ui.text( + "Insufficient funds. Try the faucet!", + TextDecl { + h: ui.scale(20.0), + colour, + align: AlignX::Center, + ..TextDecl + }, + ); } const ONE_cTAZ: u64 = 100_000_000; - let waiting_for_stake_to_finalizer = wallet_state.lock().unwrap().waiting_for_stake_to_finalizer; + let waiting_for_stake_to_finalizer = + wallet_state.lock().unwrap().waiting_for_stake_to_finalizer; { let balance = wallet_state.lock().unwrap().balance; @@ -867,8 +1402,8 @@ pub fn ui_left_pane(ui: &mut Context, }; let mut buf = [0u8; 32]; for i in 0..32 { - let a = data.get(2*i)?; - let b = data.get(2*i + 1)?; + let a = data.get(2 * i)?; + let b = data.get(2 * i + 1)?; let a = VALS[*a as usize]; if a == 0xff { return None; @@ -877,17 +1412,37 @@ pub fn ui_left_pane(ui: &mut Context, if b == 0xff { return None; } - buf[31-i] = (a << 4) | b + buf[31 - i] = (a << 4) | b } Some(buf) } let hex_dest = addr_from_str_bytes(data.stake_address.as_bytes()); let can = !waiting_for_stake_to_finalizer && hex_dest.is_some(); - if button_ex(ui, "+0.01 cTAZ", can && (balance as u64) >= ONE_cTAZ / 100) { wallet_state.lock().unwrap().stake_to_finalizer(ONE_cTAZ / 100, hex_dest.unwrap()); } - if button_ex(ui, "+0.1 cTAZ", can && (balance as u64) >= ONE_cTAZ / 10) { wallet_state.lock().unwrap().stake_to_finalizer(ONE_cTAZ / 10, hex_dest.unwrap()); } - if button_ex(ui, "+1 cTAZ", can && (balance as u64) >= ONE_cTAZ) { wallet_state.lock().unwrap().stake_to_finalizer(ONE_cTAZ, hex_dest.unwrap()); } - if button_ex(ui, "+10 cTAZ", can && (balance as u64) >= ONE_cTAZ * 10) { wallet_state.lock().unwrap().stake_to_finalizer(ONE_cTAZ * 10, hex_dest.unwrap()); } + if button_ex(ui, "+0.01 cTAZ", can && (balance as u64) >= ONE_cTAZ / 100) { + wallet_state + .lock() + .unwrap() + .stake_to_finalizer(ONE_cTAZ / 100, hex_dest.unwrap()); + } + if button_ex(ui, "+0.1 cTAZ", can && (balance as u64) >= ONE_cTAZ / 10) { + wallet_state + .lock() + .unwrap() + .stake_to_finalizer(ONE_cTAZ / 10, hex_dest.unwrap()); + } + if button_ex(ui, "+1 cTAZ", can && (balance as u64) >= ONE_cTAZ) { + wallet_state + .lock() + .unwrap() + .stake_to_finalizer(ONE_cTAZ, hex_dest.unwrap()); + } + if button_ex(ui, "+10 cTAZ", can && (balance as u64) >= ONE_cTAZ * 10) { + wallet_state + .lock() + .unwrap() + .stake_to_finalizer(ONE_cTAZ * 10, hex_dest.unwrap()); + } } } Modal::Unstake => { @@ -921,7 +1476,8 @@ pub fn ui_left_pane(ui: &mut Context, let mut button_ex = |ui: &mut Context, label, act_on_press, enabled: bool| { let id = id(label); - let (clicked, colour, text_colour) = ui.button_ex(act_on_press, BUTTON_GREY, id, enabled, false); + let (clicked, colour, text_colour) = + ui.button_ex(act_on_press, BUTTON_GREY, id, enabled, false); if let _ = elem().decl(Decl { id, child_gap, @@ -940,23 +1496,37 @@ pub fn ui_left_pane(ui: &mut Context, child_gap, radius: radius.dup4(), align: Center, - width: fit!(ui.scale(192.0)), + width: fit!(ui.scale(192.0)), height: fit!(radius * 2.0), ..Decl }) { let h = ui.scale(20.0); - ui.text(label, TextDecl { h, colour: text_colour, align: AlignX::Center, ..TextDecl }); + ui.text( + label, + TextDecl { + h, + colour: text_colour, + align: AlignX::Center, + ..TextDecl + }, + ); } } clicked }; - let mut clickable_icon = |ui: &mut Context, id, icon, icon_hovered, enabled | { - let (clicked, colour, _) = ui.button_ex(true, (0xcc, 0xcc, 0xcc, 0xff) /* @todo colors */, id, enabled, true); + let mut clickable_icon = |ui: &mut Context, id, icon, icon_hovered, enabled| { + let (clicked, colour, _) = ui.button_ex( + true, + (0xcc, 0xcc, 0xcc, 0xff), /* @todo colors */ + id, + enabled, + true, + ); let icon = if ui.hovered(id) { icon_hovered } else { icon }; - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id, child_gap, align: Center, @@ -965,13 +1535,21 @@ pub fn ui_left_pane(ui: &mut Context, height: fit!(), ..Decl }) { - ui.text(icon, TextDecl { font: Icons, colour, h: ui.scale(24.0), align: AlignX::Center, ..TextDecl }); + ui.text( + icon, + TextDecl { + font: Icons, + colour, + h: ui.scale(24.0), + align: AlignX::Center, + ..TextDecl + }, + ); } clicked }; - let staked_roster = wallet_state.lock().unwrap().staked_roster.clone(); if staked_roster.len() == 0 { ui.unstake_scroll = 0.0; @@ -979,13 +1557,14 @@ pub fn ui_left_pane(ui: &mut Context, let id = id("Unstake Scroll Container"); if ui.hovered(id) { - ui.unstake_scroll -= ui.input().zoom_delta as f32 * 32.0; + ui.unstake_scroll -= ui.input().zoom_delta as f32 * 32.0; ui.unstake_scroll -= ui.input().scroll_delta.1 as f32 * 32.0; } if ui.unstake_scroll < 0.0 { ui.unstake_scroll = 0.0; } - let scroll_container_data: clay::Clay_ScrollContainerData = unsafe { clay::Clay_GetScrollContainerData(id.clay().id) }; + let scroll_container_data: clay::Clay_ScrollContainerData = + unsafe { clay::Clay_GetScrollContainerData(id.clay().id) }; if scroll_container_data.found { let max = scroll_container_data.contentDimensions.height / ui.scale - 96.0; if ui.unstake_scroll > max { @@ -995,9 +1574,10 @@ pub fn ui_left_pane(ui: &mut Context, if let _ = elem().decl(Decl { id, colour: TRANSACTION_HISTORY_CONTAINER_COL, - child_gap: child_gap * 0.5, padding, + child_gap: child_gap * 0.5, + padding, radius: padding.0.dup4(), - width: percent!(1.0), + width: percent!(1.0), height: grow!(), // height: percent!(1.0), direction: TopToBottom, @@ -1009,22 +1589,39 @@ pub fn ui_left_pane(ui: &mut Context, let h = ui.scale(24.0); if let _ = elem().decl(Decl { direction: TopToBottom, - width: percent!(1.0), + width: percent!(1.0), height: percent!(1.0), child_gap, - align: Center, + align: Center, ..Decl }) { - ui.text(ICON_EYE_OFF, TextDecl { font: Icons, colour: WHITE.mul(0.6), h: ui.scale(64.0), align: AlignX::Center, ..TextDecl }); - ui.text("You have not staked to any finalizers yet.", TextDecl { colour: WHITE.mul(0.6), h, align: AlignX::Center, ..TextDecl }); + ui.text( + ICON_EYE_OFF, + TextDecl { + font: Icons, + colour: WHITE.mul(0.6), + h: ui.scale(64.0), + align: AlignX::Center, + ..TextDecl + }, + ); + ui.text( + "You have not staked to any finalizers yet.", + TextDecl { + colour: WHITE.mul(0.6), + h, + align: AlignX::Center, + ..TextDecl + }, + ); } - } - else { + } else { let kind_text_h = ui.scale(18.0); let transaction_text_h = ui.scale(16.0); for (index, member) in staked_roster.iter().enumerate() { - if index > 0 { // separator + if index > 0 { + // separator let colour = { let mut col = TRANSACTION_HISTORY_CONTAINER_COL; col = col.hsva(); @@ -1032,9 +1629,14 @@ pub fn ui_left_pane(ui: &mut Context, col.rgba() }; - let _ = elem().decl(Decl { colour, height: fixed!(ui.scale(2.0)), width: percent!(1.0), ..Decl }); + let _ = elem().decl(Decl { + colour, + height: fixed!(ui.scale(2.0)), + width: percent!(1.0), + ..Decl + }); } - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Unstake Roster Member", index as u32), padding, child_gap, @@ -1045,21 +1647,33 @@ pub fn ui_left_pane(ui: &mut Context, ..Decl }) { // left icon - if let _ = elem().decl(Decl{ - id: id_index("Unstake Roster Member Left Icon", index as u32), + if let _ = elem().decl(Decl { + id: id_index( + "Unstake Roster Member Left Icon", + index as u32, + ), height: fit!(), width: fixed!(ui.scale(32.0)), direction: TopToBottom, align: Center, ..Decl }) { - if clickable_icon(ui, id_index("Unstake Button", index as u32), ICON_LINK_1, ICON_UNLINK, true) { - wallet_state.lock().unwrap().unstake_from_finalizer(member.1); + if clickable_icon( + ui, + id_index("Unstake Button", index as u32), + ICON_LINK_1, + ICON_UNLINK, + true, + ) { + wallet_state + .lock() + .unwrap() + .unstake_from_finalizer(member.1); } } // info - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Unstake Roster Member Info", index as u32), height: fit!(), width: grow!(), @@ -1081,11 +1695,19 @@ pub fn ui_left_pane(ui: &mut Context, chunks }; - ui.text(frame_strf!(data, "{}", display_str(&chunks)), TextDecl { font: Mono, h: ui.scale(14.0), align: AlignX::Left, ..TextDecl }); + ui.text( + frame_strf!(data, "{}", display_str(&chunks)), + TextDecl { + font: Mono, + h: ui.scale(14.0), + align: AlignX::Left, + ..TextDecl + }, + ); } // right info - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Unstake Roster Member Amounts", index as u32), height: fit!(), width: fit!(), @@ -1101,7 +1723,12 @@ pub fn ui_left_pane(ui: &mut Context, let id = id_index("Unstake Clickable Text", index as u32); let mut colour = (0xff, 0xaf, 0x0e, 0xff); // @todo color - let mut str = frame_strf!(data, "{}.{} cTAZ", full, &part_str[..trim_part.len().max(3)]); + let mut str = frame_strf!( + data, + "{}.{} cTAZ", + full, + &part_str[..trim_part.len().max(3)] + ); if ui.hovered(id) { let stake_amount: i64 = member.2 as i64; let full = stake_amount / 100_000_000; @@ -1110,16 +1737,30 @@ pub fn ui_left_pane(ui: &mut Context, let trim_part = part_str.trim_end_matches("0"); colour = WHITE; - str = frame_strf!(data, "{}.{} cTAZ", full, &part_str[..trim_part.len().max(3)]); + str = frame_strf!( + data, + "{}.{} cTAZ", + full, + &part_str[..trim_part.len().max(3)] + ); } - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id, width: fit!(), height: fit!(), ..Decl }) { - ui.text(str, TextDecl { font: Mono, h: ui.scale(14.0), colour, align: AlignX::Right, ..TextDecl }); + ui.text( + str, + TextDecl { + font: Mono, + h: ui.scale(14.0), + colour, + align: AlignX::Right, + ..TextDecl + }, + ); } } } @@ -1143,14 +1784,20 @@ pub fn ui_left_pane(ui: &mut Context, align: Center, ..Decl }) { - tab_id_wallet = ui.tab((radius.0, 0.0, radius.2, radius.3), padding, tab_id, "Wallet"); + tab_id_wallet = ui.tab( + (radius.0, 0.0, radius.2, radius.3), + padding, + tab_id, + "Wallet", + ); } // Main contents if let _ = elem().decl(Decl { id: id("Main Contents"), colour: PANE_COL, - padding, child_gap, + padding, + child_gap, radius: (0.0, 0.0, radius.2, 0.0), direction: TopToBottom, align: Top, @@ -1160,17 +1807,12 @@ pub fn ui_left_pane(ui: &mut Context, ..Decl }) { let balance_text_h = ui.scale(48.0); - let accent_text_h = ui.scale(16.0); + let accent_text_h = ui.scale(16.0); // spacer // if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(16.0)), ..Default::default() }) {} - let ( - balance, - pending_balance, - staked_balance, - show_staked_balance, - ) = { + let (balance, pending_balance, staked_balance, show_staked_balance) = { let wallet_state = wallet_state.lock().unwrap(); ( wallet_state.balance, @@ -1182,10 +1824,10 @@ pub fn ui_left_pane(ui: &mut Context, // balance container let mut balance = balance; - let mut colour = WHITE; + let mut colour = WHITE; if show_staked_balance { balance = staked_balance; - colour = (0xff, 0xaf, 0x0e, 0xff); + colour = (0xff, 0xaf, 0x0e, 0xff); } let balance_id = id("Balance Text"); @@ -1202,9 +1844,26 @@ pub fn ui_left_pane(ui: &mut Context, align: Center, ..Decl }) { - let suffix = if !show_staked_balance && staked_balance > 0 { "*" } else { "" }; - let balance_str = frame_strf!(data, "{} cTAZ{}", str_from_ctaz(balance.try_into().unwrap()), suffix); - ui.text(&balance_str, TextDecl { colour, h: balance_text_h, align: AlignX::Center, ..TextDecl }); + let suffix = if !show_staked_balance && staked_balance > 0 { + "*" + } else { + "" + }; + let balance_str = frame_strf!( + data, + "{} cTAZ{}", + str_from_ctaz(balance.try_into().unwrap()), + suffix + ); + ui.text( + &balance_str, + TextDecl { + colour, + h: balance_text_h, + align: AlignX::Center, + ..TextDecl + }, + ); } // pending container @@ -1217,8 +1876,20 @@ pub fn ui_left_pane(ui: &mut Context, // let staked_str = frame_strf!(data, "{} cTAZ Staked", str_from_ctaz(staked_balance.try_into().unwrap())); // ui.text(&staked_str, TextDecl { h: accent_text_h, align: AlignX::Center, colour: (0x90, 0x90, 0x90, 0xff) /* @todo colors */, ..TextDecl }); - let balance_str = frame_strf!(data, "{} cTAZ Pending", str_from_ctaz(pending_balance.try_into().unwrap())); - ui.text(&balance_str, TextDecl { h: accent_text_h, align: AlignX::Center, colour: (0x90, 0x90, 0x90, 0xff) /* @todo colors */, ..TextDecl }); + let balance_str = frame_strf!( + data, + "{} cTAZ Pending", + str_from_ctaz(pending_balance.try_into().unwrap()) + ); + ui.text( + &balance_str, + TextDecl { + h: accent_text_h, + align: AlignX::Center, + colour: (0x90, 0x90, 0x90, 0xff), /* @todo colors */ + ..TextDecl + }, + ); } // spacer @@ -1230,46 +1901,77 @@ pub fn ui_left_pane(ui: &mut Context, // buttons container if let _ = elem().decl(Decl { id: id("Buttons Container"), - padding, child_gap, align: Center, + padding, + child_gap, + align: Center, width: grow!(), height: fit!(), ..Decl }) { - let mut button = |ui: &mut Context, icon: &'static str, label: &'static str| { let id = id(label); - let (clicked, colour, text_colour) = ui.button_ex(true, BUTTON_BLUE, id, true, false); + let (clicked, colour, text_colour) = + ui.button_ex(true, BUTTON_BLUE, id, true, false); if let _ = elem().decl(Decl { - id, child_gap, align: Center, + id, + child_gap, + align: Center, direction: TopToBottom, width: fit!(), height: fit!(), ..Decl }) { - let radius = ui.scale(24.0); // Button circle if let _ = elem().decl(Decl { - colour, radius: radius.dup4(), padding, child_gap, align: Center, - width: fixed!(radius * 2.0), + colour, + radius: radius.dup4(), + padding, + child_gap, + align: Center, + width: fixed!(radius * 2.0), height: fixed!(radius * 2.0), ..Decl }) { let temp_letter_symbol_h = ui.scale(28.0); - ui.text(icon, TextDecl { colour: text_colour, font: Icons, h: temp_letter_symbol_h, align: AlignX::Center, ..TextDecl }); + ui.text( + icon, + TextDecl { + colour: text_colour, + font: Icons, + h: temp_letter_symbol_h, + align: AlignX::Center, + ..TextDecl + }, + ); } let button_text_h = ui.scale(16.0); - ui.text(label, TextDecl { h: button_text_h, align: AlignX::Center, ..TextDecl }); + ui.text( + label, + TextDecl { + h: button_text_h, + align: AlignX::Center, + ..TextDecl + }, + ); } clicked }; - if button(ui, ICON_UP_BIG, "Send") { ui.modal = Modal::Send; } - if button(ui, ICON_QRCODE, "Receive") { ui.modal = Modal::Receive; } - if button(ui, ICON_LINK_1, "Stake") { ui.modal = Modal::Stake; } - if button(ui, ICON_UNLINK, "Unstake") { ui.modal = Modal::Unstake; } + if button(ui, ICON_UP_BIG, "Send") { + ui.modal = Modal::Send; + } + if button(ui, ICON_QRCODE, "Receive") { + ui.modal = Modal::Receive; + } + if button(ui, ICON_LINK_1, "Stake") { + ui.modal = Modal::Stake; + } + if button(ui, ICON_UNLINK, "Unstake") { + ui.modal = Modal::Unstake; + } } // if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(32.0)), ..Default::default() }) {} @@ -1283,13 +1985,14 @@ pub fn ui_left_pane(ui: &mut Context, let id = id("History Scroll Container"); if ui.hovered(id) { - ui.history_scroll -= ui.input().zoom_delta as f32 * 32.0; + ui.history_scroll -= ui.input().zoom_delta as f32 * 32.0; ui.history_scroll -= ui.input().scroll_delta.1 as f32 * 32.0; } if ui.history_scroll < 0.0 { ui.history_scroll = 0.0; } - let scroll_container_data: clay::Clay_ScrollContainerData = unsafe { clay::Clay_GetScrollContainerData(id.clay().id) }; + let scroll_container_data: clay::Clay_ScrollContainerData = + unsafe { clay::Clay_GetScrollContainerData(id.clay().id) }; if scroll_container_data.found { let max = scroll_container_data.contentDimensions.height / ui.scale - 96.0; if ui.history_scroll > max { @@ -1299,9 +2002,10 @@ pub fn ui_left_pane(ui: &mut Context, if let _ = elem().decl(Decl { id, colour: TRANSACTION_HISTORY_CONTAINER_COL, - child_gap: child_gap * 0.5, padding, + child_gap: child_gap * 0.5, + padding, radius: padding.0.dup4(), - width: percent!(1.0), + width: percent!(1.0), height: grow!(), // height: percent!(1.0), direction: TopToBottom, @@ -1313,22 +2017,39 @@ pub fn ui_left_pane(ui: &mut Context, let h = ui.scale(24.0); if let _ = elem().decl(Decl { direction: TopToBottom, - width: percent!(1.0), + width: percent!(1.0), height: percent!(1.0), child_gap, - align: Center, + align: Center, ..Decl }) { - ui.text(ICON_DROPBOX_1, TextDecl { font: Icons, colour: WHITE.mul(0.6), h: ui.scale(64.0), align: AlignX::Center, ..TextDecl }); - ui.text("There are no transactions yet.", TextDecl { colour: WHITE.mul(0.6), h, align: AlignX::Center, ..TextDecl }); + ui.text( + ICON_DROPBOX_1, + TextDecl { + font: Icons, + colour: WHITE.mul(0.6), + h: ui.scale(64.0), + align: AlignX::Center, + ..TextDecl + }, + ); + ui.text( + "There are no transactions yet.", + TextDecl { + colour: WHITE.mul(0.6), + h, + align: AlignX::Center, + ..TextDecl + }, + ); } - } - else { + } else { let kind_text_h = ui.scale(18.0); let transaction_text_h = ui.scale(16.0); for (index, tx) in txs.iter().enumerate() { - if index > 0 { // separator + if index > 0 { + // separator let colour = { let mut col = TRANSACTION_HISTORY_CONTAINER_COL; col = col.hsva(); @@ -1336,9 +2057,14 @@ pub fn ui_left_pane(ui: &mut Context, col.rgba() }; - let _ = elem().decl(Decl { colour, height: fixed!(ui.scale(2.0)), width: percent!(1.0), ..Decl }); + let _ = elem().decl(Decl { + colour, + height: fixed!(ui.scale(2.0)), + width: percent!(1.0), + ..Decl + }); } - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Transaction", index as u32), padding, child_gap, @@ -1349,7 +2075,7 @@ pub fn ui_left_pane(ui: &mut Context, ..Decl }) { // left icon - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Left Icon", index as u32), height: fit!(), width: fixed!(ui.scale(32.0)), @@ -1358,10 +2084,10 @@ pub fn ui_left_pane(ui: &mut Context, ..Decl }) { let icon = match (tx.1, tx.0.mined_height) { - (wallet::WalletTxKind::Send, Some(_)) => ICON_UP_SMALL, + (wallet::WalletTxKind::Send, Some(_)) => ICON_UP_SMALL, (wallet::WalletTxKind::Receive, Some(_)) => ICON_DOWN_SMALL, - (wallet::WalletTxKind::Shield, Some(_)) => ICON_SHIELD, - (wallet::WalletTxKind::Stake, Some(_)) => ICON_LINK_1, + (wallet::WalletTxKind::Shield, Some(_)) => ICON_SHIELD, + (wallet::WalletTxKind::Stake, Some(_)) => ICON_LINK_1, (wallet::WalletTxKind::Unstake, Some(_)) => ICON_UNLINK, _ => { let timer = (ui.tx_loading_animation_timer * 3.0) as u64; @@ -1381,11 +2107,20 @@ pub fn ui_left_pane(ui: &mut Context, (0x60, 0x60, 0x60, 0xff) /* @todo colors */ }; - ui.text(icon, TextDecl { font: Icons, colour, h: ui.scale(24.0), align: AlignX::Center, ..TextDecl }); + ui.text( + icon, + TextDecl { + font: Icons, + colour, + h: ui.scale(24.0), + align: AlignX::Center, + ..TextDecl + }, + ); } // info - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Centre Info", index as u32), height: fit!(), width: grow!(), @@ -1394,11 +2129,41 @@ pub fn ui_left_pane(ui: &mut Context, ..Decl }) { let label = match tx.1 { - wallet::WalletTxKind::Send => if tx.0.mined_height.is_some() { "Sent" } else { "Sending" }, - wallet::WalletTxKind::Receive => if tx.0.mined_height.is_some() { "Received" } else { "Receiving" }, - wallet::WalletTxKind::Shield => if tx.0.mined_height.is_some() { "Shielded" } else { "Shielding" }, - wallet::WalletTxKind::Stake => if tx.0.mined_height.is_some() { "Staked" } else { "Staking" }, - wallet::WalletTxKind::Unstake => if tx.0.mined_height.is_some() { "Unstaked" } else { "Unstaking" }, + wallet::WalletTxKind::Send => { + if tx.0.mined_height.is_some() { + "Sent" + } else { + "Sending" + } + } + wallet::WalletTxKind::Receive => { + if tx.0.mined_height.is_some() { + "Received" + } else { + "Receiving" + } + } + wallet::WalletTxKind::Shield => { + if tx.0.mined_height.is_some() { + "Shielded" + } else { + "Shielding" + } + } + wallet::WalletTxKind::Stake => { + if tx.0.mined_height.is_some() { + "Staked" + } else { + "Staking" + } + } + wallet::WalletTxKind::Unstake => { + if tx.0.mined_height.is_some() { + "Unstaked" + } else { + "Unstaking" + } + } }; let label_str = if let Some(mined_height) = tx.0.mined_height { @@ -1407,19 +2172,55 @@ pub fn ui_left_pane(ui: &mut Context, frame_strf!(data, "{}", label) }; - ui.text(label_str, TextDecl { h: kind_text_h, align: AlignX::Left, ..TextDecl }); + ui.text( + label_str, + TextDecl { + h: kind_text_h, + align: AlignX::Left, + ..TextDecl + }, + ); // spacer - if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(4.0)), ..Default::default() }) {} + if let _ = elem().decl(Decl { + width: grow!(), + height: fixed!(ui.scale(4.0)), + ..Default::default() + }) {} let txid = tx.0.txid.to_string(); - ui.text(frame_strf!(data, "{}..{}", &txid[0..8], &txid[txid.len() - 8..]), TextDecl { font: Mono, h: transaction_text_h, colour: (0x90, 0x90, 0x90, 0xff) /* @todo colors */, align: AlignX::Left, ..TextDecl }); + ui.text( + frame_strf!( + data, + "{}..{}", + &txid[0..8], + &txid[txid.len() - 8..] + ), + TextDecl { + font: Mono, + h: transaction_text_h, + colour: (0x90, 0x90, 0x90, 0xff), /* @todo colors */ + align: AlignX::Left, + ..TextDecl + }, + ); // spacer - if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(4.0)), ..Default::default() }) {} - - if let Ok(memo_str) = String::from_utf8(tx.0.memo.as_slice().to_vec()) { - let mut memo_str = memo_str.chars().filter(|c| c.is_ascii()).collect::().trim_end_matches(|c| c == '\0').to_string(); + if let _ = elem().decl(Decl { + width: grow!(), + height: fixed!(ui.scale(4.0)), + ..Default::default() + }) {} + + if let Ok(memo_str) = + String::from_utf8(tx.0.memo.as_slice().to_vec()) + { + let mut memo_str = memo_str + .chars() + .filter(|c| c.is_ascii()) + .collect::() + .trim_end_matches(|c| c == '\0') + .to_string(); if memo_str.len() != 0 { if memo_str.starts_with("@") { if let Some(end) = memo_str.find(":") { @@ -1427,13 +2228,21 @@ pub fn ui_left_pane(ui: &mut Context, } } - ui.text(frame_strf!(data, "{}", memo_str), TextDecl { h: transaction_text_h, align: AlignX::Left, colour: (0x90, 0x90, 0x90, 0xff) /* @todo colors */, ..TextDecl }); + ui.text( + frame_strf!(data, "{}", memo_str), + TextDecl { + h: transaction_text_h, + align: AlignX::Left, + colour: (0x90, 0x90, 0x90, 0xff), /* @todo colors */ + ..TextDecl + }, + ); } } } // right info - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Right Info", index as u32), height: fit!(), width: fit!(), @@ -1443,10 +2252,11 @@ pub fn ui_left_pane(ui: &mut Context, }) { // @todo colors let color = match tx.1 { - wallet::WalletTxKind::Send => (0xec, 0x27, 0x3f, 0xff), - wallet::WalletTxKind::Stake => (0xff, 0xaf, 0x0e, 0xff), - wallet::WalletTxKind::Receive | wallet::WalletTxKind::Unstake => (0x5a, 0xb5, 0x52, 0xff), - wallet::WalletTxKind::Shield => (0x33, 0x88, 0xde, 0xff), + wallet::WalletTxKind::Send => (0xec, 0x27, 0x3f, 0xff), + wallet::WalletTxKind::Stake => (0xff, 0xaf, 0x0e, 0xff), + wallet::WalletTxKind::Receive + | wallet::WalletTxKind::Unstake => (0x5a, 0xb5, 0x52, 0xff), + wallet::WalletTxKind::Shield => (0x33, 0x88, 0xde, 0xff), _ => WHITE, }; @@ -1461,12 +2271,43 @@ pub fn ui_left_pane(ui: &mut Context, part_str = part_str[..3].to_string(); } - let prefix = if tx.1 == wallet::WalletTxKind::Send { "-" } else { "" }; - ui.text(frame_strf!(data, "{}{}.{} cTAZ", prefix, full, part_str), TextDecl { h: transaction_text_h, align: AlignX::Right, colour: color, ..TextDecl }); - }, - wallet::WalletTxKind::Receive | wallet::WalletTxKind::Unstake => { - ui.text(frame_strf!(data, "+{} cTAZ", str_from_ctaz(tx.0.total_received.into_u64())), TextDecl { h: transaction_text_h, align: AlignX::Right, colour: color, ..TextDecl }); - }, + let prefix = if tx.1 == wallet::WalletTxKind::Send { + "-" + } else { + "" + }; + ui.text( + frame_strf!( + data, + "{}{}.{} cTAZ", + prefix, + full, + part_str + ), + TextDecl { + h: transaction_text_h, + align: AlignX::Right, + colour: color, + ..TextDecl + }, + ); + } + wallet::WalletTxKind::Receive + | wallet::WalletTxKind::Unstake => { + ui.text( + frame_strf!( + data, + "+{} cTAZ", + str_from_ctaz(tx.0.total_received.into_u64()) + ), + TextDecl { + h: transaction_text_h, + align: AlignX::Right, + colour: color, + ..TextDecl + }, + ); + } wallet::WalletTxKind::Shield => { let shield_amount: i64 = tx.0.account_value_delta.into(); let full = shield_amount / 100_000_000; @@ -1474,11 +2315,32 @@ pub fn ui_left_pane(ui: &mut Context, let part_str = format!("{part}00"); let trim_part = part_str.trim_end_matches("0"); - ui.text(frame_strf!(data, "{}.{} cTAZ", full, &part_str[..trim_part.len().max(3)]), TextDecl { h: transaction_text_h, align: AlignX::Right, colour: color, ..TextDecl }); - }, + ui.text( + frame_strf!( + data, + "{}.{} cTAZ", + full, + &part_str[..trim_part.len().max(3)] + ), + TextDecl { + h: transaction_text_h, + align: AlignX::Right, + colour: color, + ..TextDecl + }, + ); + } _ => { - ui.text("TODO cTAZ", TextDecl { h: transaction_text_h, align: AlignX::Right, colour: color, ..TextDecl }); - }, + ui.text( + "TODO cTAZ", + TextDecl { + h: transaction_text_h, + align: AlignX::Right, + colour: color, + ..TextDecl + }, + ); + } } } } @@ -1489,14 +2351,16 @@ pub fn ui_left_pane(ui: &mut Context, } } -pub fn ui_right_pane(ui: &mut Context, - wallet_state: Arc>, - viz: &mut VizState, - data: &mut UiData, - child_gap: f32, - padding: (f32, f32, f32, f32), - radius: (f32, f32, f32, f32), - tab_id: &mut Id) { +pub fn ui_right_pane( + ui: &mut Context, + wallet_state: Arc>, + viz: &mut VizState, + data: &mut UiData, + child_gap: f32, + padding: (f32, f32, f32, f32), + radius: (f32, f32, f32, f32), + tab_id: &mut Id, +) { // @TODO: MAKE THESE NOT USE TABS, JUST USE HEADERS let mut tab_id_faucet = Id::default(); let mut tab_id_roster = Id::default(); @@ -1511,11 +2375,18 @@ pub fn ui_right_pane(ui: &mut Context, align: Center, ..Decl }) { - tab_id_roster = ui.tab_ex((0.0, radius.1, radius.2, radius.3), padding, tab_id, id("Finalizers"), frame_strf!(data, "Finalizers ({})", roster.len())); + tab_id_roster = ui.tab_ex( + (0.0, radius.1, radius.2, radius.3), + padding, + tab_id, + id("Finalizers"), + frame_strf!(data, "Finalizers ({})", roster.len()), + ); } if let _ = elem().decl(Decl { id: id("Finalizers Contents"), - padding, child_gap, + padding, + child_gap, radius: (0.0, 0.0, 0.0, radius.3), colour: PANE_COL, direction: TopToBottom, @@ -1551,7 +2422,8 @@ pub fn ui_right_pane(ui: &mut Context, let mut button_ex = |ui: &mut Context, label, act_on_press, enabled: bool| { let id = id(label); - let (clicked, colour, text_colour) = ui.button_ex(act_on_press, BUTTON_GREY, id, enabled, false); + let (clicked, colour, text_colour) = + ui.button_ex(act_on_press, BUTTON_GREY, id, enabled, false); if let _ = elem().decl(Decl { id, child_gap, @@ -1570,21 +2442,35 @@ pub fn ui_right_pane(ui: &mut Context, child_gap, radius: radius.dup4(), align: Center, - width: fit!(ui.scale(192.0)), + width: fit!(ui.scale(192.0)), height: fit!(radius * 2.0), ..Decl }) { let h = ui.scale(20.0); - ui.text(label, TextDecl { h, colour: text_colour, align: AlignX::Center, ..TextDecl }); + ui.text( + label, + TextDecl { + h, + colour: text_colour, + align: AlignX::Center, + ..TextDecl + }, + ); } } clicked }; - let mut clickable_icon = |ui: &mut Context, id, icon, enabled | { - let (clicked, colour, _) = ui.button_ex(false, (0xcc, 0xcc, 0xcc, 0xff) /* @todo colors */, id, enabled, true); - if let _ = elem().decl(Decl{ + let mut clickable_icon = |ui: &mut Context, id, icon, enabled| { + let (clicked, colour, _) = ui.button_ex( + false, + (0xcc, 0xcc, 0xcc, 0xff), /* @todo colors */ + id, + enabled, + true, + ); + if let _ = elem().decl(Decl { id, child_gap, align: Center, @@ -1593,7 +2479,16 @@ pub fn ui_right_pane(ui: &mut Context, height: fit!(), ..Decl }) { - ui.text(icon, TextDecl { font: Icons, colour, h: ui.scale(24.0), align: AlignX::Center, ..TextDecl }); + ui.text( + icon, + TextDecl { + font: Icons, + colour, + h: ui.scale(24.0), + align: AlignX::Center, + ..TextDecl + }, + ); } clicked @@ -1602,7 +2497,9 @@ pub fn ui_right_pane(ui: &mut Context, // @todo(judah): incorporate into list below? (i.e. always at the top of the list (and doesn't scroll with the rest), styled different from the others) if let _ = elem().decl(Decl { id: id("Finalizers Buttons"), - padding, child_gap, align: Center, + padding, + child_gap, + align: Center, direction: TopToBottom, width: percent!(1.0), height: fit!(), @@ -1613,8 +2510,30 @@ pub fn ui_right_pane(ui: &mut Context, recv_address.push_str(&format!("{:02x}", b)); } - ui.text("Your Finalizer Address", TextDecl { h: ui.scale(20.0), colour: WHITE, align: AlignX::Center, ..TextDecl }); - ui.text(frame_strf!(data, "[{}..{}]", &recv_address[0..8], &recv_address[recv_address.len()-8..]), TextDecl { font: Mono, h: ui.scale(20.0), colour: WHITE, align: AlignX::Center, ..TextDecl }); + ui.text( + "Your Finalizer Address", + TextDecl { + h: ui.scale(20.0), + colour: WHITE, + align: AlignX::Center, + ..TextDecl + }, + ); + ui.text( + frame_strf!( + data, + "[{}..{}]", + &recv_address[0..8], + &recv_address[recv_address.len() - 8..] + ), + TextDecl { + font: Mono, + h: ui.scale(20.0), + colour: WHITE, + align: AlignX::Center, + ..TextDecl + }, + ); if button_ex(ui, "Copy Address", true, true) { ui.input().send_to_clipboard(&recv_address); @@ -1627,13 +2546,14 @@ pub fn ui_right_pane(ui: &mut Context, let id = id("History Scroll Container"); if ui.hovered(id) { - ui.finalizers_scroll -= ui.input().zoom_delta as f32 * 32.0; + ui.finalizers_scroll -= ui.input().zoom_delta as f32 * 32.0; ui.finalizers_scroll -= ui.input().scroll_delta.1 as f32 * 32.0; } if ui.finalizers_scroll < 0.0 { ui.finalizers_scroll = 0.0; } - let scroll_container_data: clay::Clay_ScrollContainerData = unsafe { clay::Clay_GetScrollContainerData(id.clay().id) }; + let scroll_container_data: clay::Clay_ScrollContainerData = + unsafe { clay::Clay_GetScrollContainerData(id.clay().id) }; if scroll_container_data.found { let max = scroll_container_data.contentDimensions.height / ui.scale - 96.0; if ui.finalizers_scroll > max { @@ -1643,9 +2563,10 @@ pub fn ui_right_pane(ui: &mut Context, if let _ = elem().decl(Decl { id, colour: TRANSACTION_HISTORY_CONTAINER_COL, - child_gap: child_gap * 0.5, padding, + child_gap: child_gap * 0.5, + padding, radius: padding.0.dup4(), - width: percent!(1.0), + width: percent!(1.0), height: grow!(), // height: percent!(1.0), direction: TopToBottom, @@ -1657,22 +2578,39 @@ pub fn ui_right_pane(ui: &mut Context, let h = ui.scale(24.0); if let _ = elem().decl(Decl { direction: TopToBottom, - width: percent!(1.0), + width: percent!(1.0), height: percent!(1.0), child_gap, - align: Center, + align: Center, ..Decl }) { - ui.text(ICON_EYE_OFF, TextDecl { font: Icons, colour: WHITE.mul(0.6), h: ui.scale(64.0), align: AlignX::Center, ..TextDecl }); - ui.text("There are no finalizers yet.", TextDecl { colour: WHITE.mul(0.6), h, align: AlignX::Center, ..TextDecl }); + ui.text( + ICON_EYE_OFF, + TextDecl { + font: Icons, + colour: WHITE.mul(0.6), + h: ui.scale(64.0), + align: AlignX::Center, + ..TextDecl + }, + ); + ui.text( + "There are no finalizers yet.", + TextDecl { + colour: WHITE.mul(0.6), + h, + align: AlignX::Center, + ..TextDecl + }, + ); } - } - else { + } else { let kind_text_h = ui.scale(18.0); let transaction_text_h = ui.scale(16.0); for (index, member) in roster.iter().enumerate() { - if index > 0 { // separator + if index > 0 { + // separator let colour = { let mut col = TRANSACTION_HISTORY_CONTAINER_COL; col = col.hsva(); @@ -1680,9 +2618,14 @@ pub fn ui_right_pane(ui: &mut Context, col.rgba() }; - let _ = elem().decl(Decl { colour, height: fixed!(ui.scale(2.0)), width: percent!(1.0), ..Decl }); + let _ = elem().decl(Decl { + colour, + height: fixed!(ui.scale(2.0)), + width: percent!(1.0), + ..Decl + }); } - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Roster Member", index as u32), padding, child_gap, @@ -1693,7 +2636,7 @@ pub fn ui_right_pane(ui: &mut Context, ..Decl }) { // left icon - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Roster Member Left Icon", index as u32), height: fit!(), width: fixed!(ui.scale(32.0)), @@ -1701,7 +2644,12 @@ pub fn ui_right_pane(ui: &mut Context, align: Center, ..Decl }) { - if clickable_icon(ui, id_index("Copy Button", index as u32), ICON_DOCS_1, true) { + if clickable_icon( + ui, + id_index("Copy Button", index as u32), + ICON_DOCS_1, + true, + ) { let mut address_str = String::new(); for b in member.pub_key.iter().rev() { address_str.push_str(&format!("{:02x}", b)); @@ -1711,7 +2659,7 @@ pub fn ui_right_pane(ui: &mut Context, } // info - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Roster Member Info", index as u32), height: fit!(), width: grow!(), @@ -1733,11 +2681,19 @@ pub fn ui_right_pane(ui: &mut Context, chunks }; - ui.text(frame_strf!(data, "{}", display_str(&chunks)), TextDecl { font: Mono, h: ui.scale(18.0), align: AlignX::Left, ..TextDecl }); + ui.text( + frame_strf!(data, "{}", display_str(&chunks)), + TextDecl { + font: Mono, + h: ui.scale(18.0), + align: AlignX::Left, + ..TextDecl + }, + ); } // right info - if let _ = elem().decl(Decl{ + if let _ = elem().decl(Decl { id: id_index("Roster Member Amounts", index as u32), height: fit!(), width: fit!(), @@ -1751,7 +2707,21 @@ pub fn ui_right_pane(ui: &mut Context, let part_str = format!("{part}00"); let trim_part = part_str.trim_end_matches("0"); let colour = (0xff, 0xaf, 0x0e, 0xff); - ui.text(frame_strf!(data, "{}.{} cTAZ", full, &part_str[..trim_part.len().max(3)]), TextDecl { font: Mono, h: ui.scale(16.0), colour, align: AlignX::Right, ..TextDecl }); + ui.text( + frame_strf!( + data, + "{}.{} cTAZ", + full, + &part_str[..trim_part.len().max(3)] + ), + TextDecl { + font: Mono, + h: ui.scale(16.0), + colour, + align: AlignX::Right, + ..TextDecl + }, + ); } } } @@ -1763,7 +2733,11 @@ pub fn ui_right_pane(ui: &mut Context, } // spacer - if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(16.0)), ..Default::default() }) {} + if let _ = elem().decl(Decl { + width: grow!(), + height: fixed!(ui.scale(16.0)), + ..Default::default() + }) {} if let _ = elem().decl(Decl { id: id("Faucet Tab Bar"), @@ -1773,11 +2747,22 @@ pub fn ui_right_pane(ui: &mut Context, align: Center, ..Decl }) { - tab_id_faucet = ui.tab_ex(radius, padding, tab_id, id("Faucet"), frame_strf!(data, "Faucet (Height {})", &wallet_state.lock().unwrap().miner_seen_height)); + tab_id_faucet = ui.tab_ex( + radius, + padding, + tab_id, + id("Faucet"), + frame_strf!( + data, + "Faucet (Height {})", + &wallet_state.lock().unwrap().miner_seen_height + ), + ); } if let _ = elem().decl(Decl { id: id("Faucet Contents"), - padding, child_gap, + padding, + child_gap, radius: (0.0, 0.0, 0.0, radius.3), colour: PANE_COL, direction: TopToBottom, @@ -1787,7 +2772,11 @@ pub fn ui_right_pane(ui: &mut Context, ..Decl }) { // spacer - if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(16.0)), ..Default::default() }) {} + if let _ = elem().decl(Decl { + width: grow!(), + height: fixed!(ui.scale(16.0)), + ..Default::default() + }) {} // big text container // if let _ = elem().decl(Decl { @@ -1806,8 +2795,11 @@ pub fn ui_right_pane(ui: &mut Context, // info container if let _ = elem().decl(Decl { - padding: ui.scale(32.0).dup4(), child_gap, align: TopLeft, - width: grow!(), height: fit!(), + padding: ui.scale(32.0).dup4(), + child_gap, + align: TopLeft, + width: grow!(), + height: fit!(), direction: TopToBottom, ..Decl }) { @@ -1823,29 +2815,74 @@ pub fn ui_right_pane(ui: &mut Context, ) }; - let row = Decl { width: percent!(1.0), child_gap, height: fit!(), ..Decl }; + let row = Decl { + width: percent!(1.0), + child_gap, + height: fit!(), + ..Decl + }; - let left = Decl { width: grow!(), height: fit!(), align: Left, ..Decl }; - let right = Decl { width: grow!(), height: fit!(), align: Right, ..Decl }; + let left = Decl { + width: grow!(), + height: fit!(), + align: Left, + ..Decl + }; + let right = Decl { + width: grow!(), + height: fit!(), + align: Right, + ..Decl + }; - let left_text = TextDecl { h: text_h, align: AlignX::Left, ..TextDecl }; - let right_text = TextDecl { font: Mono, align: AlignX::Right, ..left_text }; + let left_text = TextDecl { + h: text_h, + align: AlignX::Left, + ..TextDecl + }; + let right_text = TextDecl { + font: Mono, + align: AlignX::Right, + ..left_text + }; if let _ = elem().decl(row) { - if let _ = elem().decl(left) { ui.text("Available:", left_text); } - if let _ = elem().decl(right) { ui.text(frame_strf!(data, "{} cTAZ", str_from_ctaz(fc)), right_text); } + if let _ = elem().decl(left) { + ui.text("Available:", left_text); + } + if let _ = elem().decl(right) { + ui.text(frame_strf!(data, "{} cTAZ", str_from_ctaz(fc)), right_text); + } } if let _ = elem().decl(row) { - if let _ = elem().decl(left) { ui.text("Unshielded:", left_text); } - if let _ = elem().decl(right) { ui.text(frame_strf!(data, "{} cTAZ", str_from_ctaz(un)), right_text); } + if let _ = elem().decl(left) { + ui.text("Unshielded:", left_text); + } + if let _ = elem().decl(right) { + ui.text(frame_strf!(data, "{} cTAZ", str_from_ctaz(un)), right_text); + } } if let _ = elem().decl(row) { - if let _ = elem().decl(left) { ui.text("Shielded (Pending):", left_text); } - if let _ = elem().decl(right) { ui.text(frame_strf!(data, "{} cTAZ", str_from_ctaz(sh_p)), right_text); } + if let _ = elem().decl(left) { + ui.text("Shielded (Pending):", left_text); + } + if let _ = elem().decl(right) { + ui.text( + frame_strf!(data, "{} cTAZ", str_from_ctaz(sh_p)), + right_text, + ); + } } if let _ = elem().decl(row) { - if let _ = elem().decl(left) { ui.text("Shielded (Spendable):", left_text); } - if let _ = elem().decl(right) { ui.text(frame_strf!(data, "{} cTAZ", str_from_ctaz(sh_s)), right_text); } + if let _ = elem().decl(left) { + ui.text("Shielded (Spendable):", left_text); + } + if let _ = elem().decl(right) { + ui.text( + frame_strf!(data, "{} cTAZ", str_from_ctaz(sh_s)), + right_text, + ); + } } // if let _ = elem().decl(Decl { width: grow!(), height: fixed!(ui.scale(32.0)), ..Default::default() }) {} }; @@ -1853,15 +2890,17 @@ pub fn ui_right_pane(ui: &mut Context, // buttons container if let _ = elem().decl(Decl { id: id("Buttons Container"), - padding, child_gap, align: Center, + padding, + child_gap, + align: Center, width: percent!(1.0), height: fit!(), ..Decl }) { - let mut button_ex = |label, act_on_press, enabled: bool| { let id = id(label); - let (clicked, colour, text_colour) = ui.button_ex(act_on_press, BUTTON_GREY, id, enabled, false); + let (clicked, colour, text_colour) = + ui.button_ex(act_on_press, BUTTON_GREY, id, enabled, false); if let _ = elem().decl(Decl { id, child_gap, @@ -1880,70 +2919,93 @@ pub fn ui_right_pane(ui: &mut Context, child_gap, radius: radius.dup4(), align: Center, - width: fit!(ui.scale(192.0)), + width: fit!(ui.scale(192.0)), height: fit!(radius * 2.0), ..Decl }) { let h = ui.scale(20.0); - ui.text(label, TextDecl { h, colour: text_colour, align: AlignX::Center, ..TextDecl }); + ui.text( + label, + TextDecl { + h, + colour: text_colour, + align: AlignX::Center, + ..TextDecl + }, + ); } } clicked }; - if button_ex("Receive cTAZ", false, !wallet_state.lock().unwrap().waiting_for_faucet) { + if button_ex( + "Receive cTAZ", + false, + !wallet_state.lock().unwrap().waiting_for_faucet, + ) { wallet_state.lock().unwrap().request_from_faucet(); } } } } - -pub fn run_ui(ui: &mut Context, wallet_state: Arc>, data: &mut UiData, viz: &mut VizState, is_rendering: bool) -> bool { -data.per_frame_strs.clear(); - -let mut result = false; - -const MIN_ZOOM: f32 = 0.5; -const MAX_ZOOM: f32 = 1.65; // @PreventPanesColliding - -if ui.input().key_held(KeyCode::ControlLeft) || ui.input().key_held(KeyCode::ControlRight) { - if ui.input().key_pressed(KeyCode::Equal) { - let new_zoom = ui.zoom * (1.0f32 + 1.0f32 / 8f32); - if new_zoom <= MAX_ZOOM { - ui.zoom = new_zoom; +pub fn run_ui( + ui: &mut Context, + wallet_state: Arc>, + data: &mut UiData, + viz: &mut VizState, + is_rendering: bool, +) -> bool { + data.per_frame_strs.clear(); + + let mut result = false; + + const MIN_ZOOM: f32 = 0.5; + const MAX_ZOOM: f32 = 1.65; // @PreventPanesColliding + + if ui.input().key_held(KeyCode::ControlLeft) || ui.input().key_held(KeyCode::ControlRight) { + if ui.input().key_pressed(KeyCode::Equal) { + let new_zoom = ui.zoom * (1.0f32 + 1.0f32 / 8f32); + if new_zoom <= MAX_ZOOM { + ui.zoom = new_zoom; + } } - } - if ui.input().key_pressed(KeyCode::Minus) { - let new_zoom = ui.zoom / (1.0f32 + 1.0f32 / 8f32); - if new_zoom >= MIN_ZOOM { - ui.zoom = new_zoom; + if ui.input().key_pressed(KeyCode::Minus) { + let new_zoom = ui.zoom / (1.0f32 + 1.0f32 / 8f32); + if new_zoom >= MIN_ZOOM { + ui.zoom = new_zoom; + } + } + if ui.input().key_pressed(KeyCode::Digit0) { + ui.zoom = 1.0f32; } } - if ui.input().key_pressed(KeyCode::Digit0) { - ui.zoom = 1.0f32; + if ui.zoom < MIN_ZOOM { + ui.zoom = 1.0f32; // reset instead of clamp to prevent user from shifting "off-grid" of the exponential steps } -} -if ui.zoom < MIN_ZOOM { - ui.zoom = 1.0f32; // reset instead of clamp to prevent user from shifting "off-grid" of the exponential steps -} -if ui.zoom > MAX_ZOOM { - ui.zoom = 1.0f32; // reset instead of clamp to prevent user from shifting "off-grid" of the exponential steps -} -ui.scale = ui.zoom * ui.dpi_scale; + if ui.zoom > MAX_ZOOM { + ui.zoom = 1.0f32; // reset instead of clamp to prevent user from shifting "off-grid" of the exponential steps + } + ui.scale = ui.zoom * ui.dpi_scale; -ui.cursor = winit::window::Cursor::Icon(winit::window::CursorIcon::Default); + ui.cursor = winit::window::Cursor::Icon(winit::window::CursorIcon::Default); -ui.capture = false; + ui.capture = false; -let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_height as f32); - let mouse_pos = (ui.input().mouse_pos().0 as f32, ui.input().mouse_pos().1 as f32); + let (window_w, window_h) = ( + ui.draw().window_width as f32, + ui.draw().window_height as f32, + ); + let mouse_pos = ( + ui.input().mouse_pos().0 as f32, + ui.input().mouse_pos().1 as f32, + ); let child_gap = ui.scale(12.0); let padding = child_gap.dup4(); - let mouse_held = ui.input().mouse_held(winit::event::MouseButton::Left); + let mouse_held = ui.input().mouse_held(winit::event::MouseButton::Left); let mouse_clicked = ui.input().mouse_pressed(winit::event::MouseButton::Left); let radius = ui.scale(12.0).dup4(); @@ -1953,7 +3015,12 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig clay.set_layout_dimensions((window_w as f32, window_h as f32).into()); clay.pointer_state(mouse_pos.into(), mouse_held); clay.set_measure_text_function_user_data(ui.draw(), |string, text_config, draw| { - let font_kind = match text_config.font_id { 0 => FontKind::Normal, 1 => FontKind::Mono, 2 => FontKind::Icons, _ => todo!() }; + let font_kind = match text_config.font_id { + 0 => FontKind::Normal, + 1 => FontKind::Mono, + 2 => FontKind::Icons, + _ => todo!(), + }; let h = text_config.font_size as f32; let w = draw.measure_text_line(font_kind, h, string); clay::math::Dimensions::new(w, h) @@ -1961,19 +3028,23 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig let mut c = clay.begin::<(), ()>(); - unsafe { clay::Clay_SetCurrentContext(c.clay.context); } - unsafe { clay::Clay_SetMaxMeasureTextCacheWordCount(262144); } + unsafe { + clay::Clay_SetCurrentContext(c.clay.context); + } + unsafe { + clay::Clay_SetMaxMeasureTextCacheWordCount(262144); + } // c.set_debug_mode(true); if let _ = elem().decl(Decl { id: id("Main"), - padding: (0.0, 0.0, padding.2, padding.3), child_gap, + padding: (0.0, 0.0, padding.2, padding.3), + child_gap, width: grow!(), height: grow!(), ..Decl }) { - let pane_pct = Sizing::Percent(ui.zoom * PANE_PERCENT); if let _elem = elem().decl(Decl { @@ -1990,13 +3061,24 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig } let mut pane_tab_l = ui.pane_tab_l; - ui_left_pane(ui, wallet_state.clone(), data, viz, child_gap, padding, radius, &mut pane_tab_l); + ui_left_pane( + ui, + wallet_state.clone(), + data, + viz, + child_gap, + padding, + radius, + &mut pane_tab_l, + ); ui.pane_tab_l = pane_tab_l; } if let _elem = elem().decl(Decl { id: id("Central Gap"), - radius, padding, child_gap, + radius, + padding, + child_gap, direction: TopToBottom, width: grow!(), height: grow!(), @@ -2021,22 +3103,45 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig ..Decl }) { // @todo: two vertical panes with right aligned label and left aligned height - ui.text(frame_strf!(data, "PoS Height: {}", viz.bft_tip_height), TextDecl { h: ui.scale(16.0), align: AlignX::Center, ..TextDecl }); - ui.text(frame_strf!(data, "PoW Height: {}", viz.bc_tip_height), TextDecl { h: ui.scale(16.0), align: AlignX::Center, ..TextDecl }); + ui.text( + frame_strf!(data, "PoS Height: {}", viz.bft_tip_height), + TextDecl { + h: ui.scale(16.0), + align: AlignX::Center, + ..TextDecl + }, + ); + ui.text( + frame_strf!(data, "PoW Height: {}", viz.bc_tip_height), + TextDecl { + h: ui.scale(16.0), + align: AlignX::Center, + ..TextDecl + }, + ); } } // spacer - if let _ = elem().decl(Decl { height: grow!(), ..Decl }) {} + if let _ = elem().decl(Decl { + height: grow!(), + ..Decl + }) {} // "Reset View" button - if let _ = elem().decl(Decl { align: Bottom, width: grow!(), ..Decl }) { + if let _ = elem().decl(Decl { + align: Bottom, + width: grow!(), + ..Decl + }) { let label = "Reset View"; - let enabled = viz.camera_x != 0.0 || viz.camera_y != viz.bc_tip_y || viz.zoom != 0.0; + let enabled = + viz.camera_x != 0.0 || viz.camera_y != viz.bc_tip_y || viz.zoom != 0.0; let id = id(label); - let (clicked, colour, text_colour) = ui.button_ex(true, BUTTON_GREY, id, enabled, false); + let (clicked, colour, text_colour) = + ui.button_ex(true, BUTTON_GREY, id, enabled, false); let radius = ui.scale(20.0); if ui.hovered(id) { @@ -2051,12 +3156,20 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig child_gap, radius: radius.dup4(), align: Center, - width: fit!(ui.scale(128.0)), + width: fit!(ui.scale(128.0)), height: fit!(radius * 2.0), ..Decl }) { let button_text_h = ui.scale(16.0); - ui.text(label, TextDecl { h: button_text_h, colour: text_colour, align: AlignX::Center, ..TextDecl }); + ui.text( + label, + TextDecl { + h: button_text_h, + colour: text_colour, + align: AlignX::Center, + ..TextDecl + }, + ); } if clicked { @@ -2067,12 +3180,33 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig } } + // Lots of finalizers in the list pushes the faucet section outside the screen + // This seems to fix it + let right_id = id("Right Pane"); + + if ui.hovered(right_id) { + ui.finalizers_scroll -= ui.input().zoom_delta as f32 * 32.0; + ui.finalizers_scroll -= ui.input().scroll_delta.1 as f32 * 32.0; + } + if ui.finalizers_scroll < 0.0 { + ui.finalizers_scroll = 0.0; + } + + let scroll_data: clay::Clay_ScrollContainerData = + unsafe { clay::Clay_GetScrollContainerData(right_id.clay().id) }; + if scroll_data.found { + let max = scroll_data.contentDimensions.height / ui.scale - 96.0; + if ui.finalizers_scroll > max { + ui.finalizers_scroll = max; + } + } + if let _elem = elem().decl(Decl { - id: id("Right Pane"), + id: right_id, direction: TopToBottom, width: pane_pct, height: grow!(), - clip: Clip, + clip: Scroll(0.0, -ui.finalizers_scroll * ui.scale), ..Decl }) { let id = _elem.decl.id; @@ -2081,7 +3215,16 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig } let mut pane_tab_r = ui.pane_tab_r; - ui_right_pane(ui, wallet_state.clone(), viz, data, child_gap, padding, radius, &mut pane_tab_r); + ui_right_pane( + ui, + wallet_state.clone(), + viz, + data, + child_gap, + padding, + radius, + &mut pane_tab_r, + ); ui.pane_tab_r = pane_tab_r; } } @@ -2092,27 +3235,43 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig if ui.hovered(id) { ui.capture = true; } - let border_colour = { let mut col = PANE_COL.hsva(); col.2 = 0x18; col.rgba() }; + let border_colour = { + let mut col = PANE_COL.hsva(); + col.2 = 0x18; + col.rgba() + }; if let _ = elem().decl(Decl { id, colour: border_colour, - child_gap, padding, radius, - width: fit!(), + child_gap, + padding, + radius, + width: fit!(), height: fit!(), floating: Floating::Root(ctx_menu_pos.0 as f32, ctx_menu_pos.1 as f32), ..Decl }) { if let _ = elem().decl(Decl { colour: PANE_COL, - child_gap, padding, radius, - width: fit!(ui.scale(192.0), ui.draw().window_width as f32 * 0.5), + child_gap, + padding, + radius, + width: fit!(ui.scale(192.0), ui.draw().window_width as f32 * 0.5), height: fit!(ui.scale(128.0)), direction: TopToBottom, ..Decl }) { let text_h = ui.scale(10.0); // Block Inspector Contents - ui.text(frame_strf!(data, "Block: {}", viz.inspecting_block_hash), TextDecl { break_word: true, h: text_h, align: AlignX::Left, ..TextDecl }); + ui.text( + frame_strf!(data, "Block: {}", viz.inspecting_block_hash), + TextDecl { + break_word: true, + h: text_h, + align: AlignX::Left, + ..TextDecl + }, + ); let text = { if let Some(text) = viz.inspect_block_json_text.as_ref() { @@ -2121,12 +3280,20 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig "Loading...".to_string() } }; - ui.text(frame_strf!(data, "{}", text), TextDecl { font: Mono, break_word: true, h: text_h, align: AlignX::Left, ..TextDecl }); + ui.text( + frame_strf!(data, "{}", text), + TextDecl { + font: Mono, + break_word: true, + h: text_h, + align: AlignX::Left, + ..TextDecl + }, + ); } } } - if !ui.input().mouse_held(winit::event::MouseButton::Left) { ui.clicked_id = Id::default(); } @@ -2152,27 +3319,44 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig color } - let x1 = (command.bounding_box.x) as isize; - let y1 = (command.bounding_box.y) as isize; - let x2 = (command.bounding_box.x + command.bounding_box.width) as isize; + let x1 = (command.bounding_box.x) as isize; + let y1 = (command.bounding_box.y) as isize; + let x2 = (command.bounding_box.x + command.bounding_box.width) as isize; let y2 = (command.bounding_box.y + command.bounding_box.height) as isize; match command.config { Rectangle(config) => { - let radius_tl = config.corner_radii.top_left as isize; - let radius_tr = config.corner_radii.top_right as isize; - let radius_bl = config.corner_radii.bottom_left as isize; + let radius_tl = config.corner_radii.top_left as isize; + let radius_tr = config.corner_radii.top_right as isize; + let radius_bl = config.corner_radii.bottom_left as isize; let radius_br = config.corner_radii.bottom_right as isize; - ui.draw().rounded_rectangle(x1, y1, x2, y2, - radius_tl, - radius_tr, - radius_bl, - radius_br, - clay_color_to_u32(config.color)); + ui.draw().rounded_rectangle( + x1, + y1, + x2, + y2, + radius_tl, + radius_tr, + radius_bl, + radius_br, + clay_color_to_u32(config.color), + ); } RenderCommandConfig::Text(config) => { - let font_kind = match config.font_id { 0 => FontKind::Normal, 1 => FontKind::Mono, 2 => FontKind::Icons, _ => todo!() }; - ui.draw().text_line(font_kind, x1 as f32, y1 as f32, config.font_size as f32, config.text, clay_color_to_u32(config.color)); + let font_kind = match config.font_id { + 0 => FontKind::Normal, + 1 => FontKind::Mono, + 2 => FontKind::Icons, + _ => todo!(), + }; + ui.draw().text_line( + font_kind, + x1 as f32, + y1 as f32, + config.font_size as f32, + config.text, + clay_color_to_u32(config.color), + ); } ScissorStart() => { ui.draw().set_scissor(x1, y1, x2, y2); @@ -2180,18 +3364,44 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig ScissorEnd() => { ui.draw().clear_scissor(); } - misc => { todo!("Unsupported clay render command: {:?}", misc) } + misc => { + todo!("Unsupported clay render command: {:?}", misc) + } } if ui.debug { let thickness = 2.0; let color = 0x80ff00ff; - let t = (thickness / 2.0) as isize; - - ui.draw().rectangle((x1-t) as f32, (y1-t) as f32, (x1+t) as f32, (y2+t) as f32, color); - ui.draw().rectangle((x2-t) as f32, (y1-t) as f32, (x2+t) as f32, (y2+t) as f32, color); - ui.draw().rectangle((x1-t) as f32, (y1-t) as f32, (x2-t) as f32, (y1+t) as f32, color); - ui.draw().rectangle((x1-t) as f32, (y2-t) as f32, (x2-t) as f32, (y2+t) as f32, color); + let t = (thickness / 2.0) as isize; + + ui.draw().rectangle( + (x1 - t) as f32, + (y1 - t) as f32, + (x1 + t) as f32, + (y2 + t) as f32, + color, + ); + ui.draw().rectangle( + (x2 - t) as f32, + (y1 - t) as f32, + (x2 + t) as f32, + (y2 + t) as f32, + color, + ); + ui.draw().rectangle( + (x1 - t) as f32, + (y1 - t) as f32, + (x2 - t) as f32, + (y1 + t) as f32, + color, + ); + ui.draw().rectangle( + (x1 - t) as f32, + (y2 - t) as f32, + (x2 - t) as f32, + (y2 + t) as f32, + color, + ); } } } @@ -2201,7 +3411,12 @@ let (window_w, window_h) = (ui.draw().window_width as f32, ui.draw().window_heig result } -pub fn ui_update(ui: &mut Context, data: &mut UiData, viz: &mut VizState, wallet_state: Arc>) -> bool { +pub fn ui_update( + ui: &mut Context, + data: &mut UiData, + viz: &mut VizState, + wallet_state: Arc>, +) -> bool { ui.tx_loading_animation_timer += ui.delta; if ui.tx_loading_animation_timer >= 1.0 { ui.tx_loading_animation_timer = 0.0; @@ -2217,17 +3432,19 @@ pub fn ui_update(ui: &mut Context, data: &mut UiData, viz: &mut VizState, wallet ..Default::default() }; - let real_input = ui.input; let result = run_ui(ui, wallet_state.clone(), data, viz, false); - ui.input = &dummy_input; let result = result || run_ui(ui, wallet_state.clone(), data, viz, true); - ui.input = real_input; + let real_input = ui.input; + let result = run_ui(ui, wallet_state.clone(), data, viz, false); + ui.input = &dummy_input; + let result = result || run_ui(ui, wallet_state.clone(), data, viz, true); + ui.input = real_input; return result; } #[derive(Debug, Default, Clone)] pub struct Context { pub input: *const InputCtx, - pub draw: *const DrawCtx, - pub clay: *mut Clay, + pub draw: *const DrawCtx, + pub clay: *mut Clay, pub cursor: winit::window::Cursor, pub prev_cursor: winit::window::Cursor, @@ -2237,15 +3454,15 @@ pub struct Context { pub draw_commands: Vec, - pub scale: f32, - pub zoom: f32, + pub scale: f32, + pub zoom: f32, pub dpi_scale: f32, - pub delta: f32, + pub delta: f32, pub capture: bool, - pub clicked_id: Id, - pub focused_id: Id, + pub clicked_id: Id, + pub focused_id: Id, pub most_recently_clicked_id: Id, pub pane_tab_l: Id, @@ -2253,9 +3470,9 @@ pub struct Context { pub modal: Modal, - pub history_scroll: f32, + pub history_scroll: f32, pub finalizers_scroll: f32, - pub unstake_scroll: f32, + pub unstake_scroll: f32, pub tx_loading_animation_timer: f32, } From fc00600531e67c094b6a72c54da31ca39cc0c693 Mon Sep 17 00:00:00 2001 From: dorianvp Date: Mon, 15 Dec 2025 20:19:51 -0300 Subject: [PATCH 2/2] fix: missing feature --- zebra-crosslink/wallet/Cargo.toml | 40 ++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/zebra-crosslink/wallet/Cargo.toml b/zebra-crosslink/wallet/Cargo.toml index fe84c4d1..9adcd7eb 100644 --- a/zebra-crosslink/wallet/Cargo.toml +++ b/zebra-crosslink/wallet/Cargo.toml @@ -8,11 +8,21 @@ secp256k1 = { workspace = true } bip39 = "2.2.0" zcash_primitives = { workspace = true } # version = "0.26.1" -zcash_client_backend = { path = "../../librustzcash/zcash_client_backend", features = ["lightwalletd-tonic"] } # version = "0.21.0" -zcash_client_memory = { path = "../../librustzcash/zcash_client_memory", features = ["orchard", "transparent-inputs"] } -zcash_client_sqlite = { path = "../../librustzcash/zcash_client_sqlite", features = ["orchard", "transparent-inputs"] } # version = "0.19.0" -zcash_keys = { workspace = true, features=["transparent-inputs"] } -zcash_protocol = { workspace = true } # version = "0.7.1" +zcash_client_backend = { path = "../../librustzcash/zcash_client_backend", features = [ + "lightwalletd-tonic", +] } # version = "0.21.0" +zcash_client_memory = { path = "../../librustzcash/zcash_client_memory", features = [ + "orchard", + "transparent-inputs", +] } +zcash_client_sqlite = { path = "../../librustzcash/zcash_client_sqlite", features = [ + "orchard", + "transparent-inputs", +] } # version = "0.19.0" +zcash_keys = { workspace = true, features = ["transparent-inputs"] } +zcash_protocol = { workspace = true, features = [ + "local-consensus", +] } # version = "0.7.1" zcash_transparent = { workspace = true } # version = "0.6.1" zcash_note_encryption = { workspace = true } # version = "0.4.1" zcash_proofs = { workspace = true, features = ["bundled-prover"] } @@ -29,13 +39,27 @@ rand_chacha = "0.3.1" rusqlite = { version = "0.37", features = ["bundled"] } prost = "0.13" -tonic = { workspace = true, features = ["transport", "channel", "tls-ring", "tls-webpki-roots"] } +tonic = { workspace = true, features = [ + "transport", + "channel", + "tls-ring", + "tls-webpki-roots", +] } tonic-build = { version = "0.13", default-features = false } rustls = { version = "0.23.31", default-features = false, features = ["ring"] } -tokio = { workspace = true, features = ["time", "rt-multi-thread", "macros", "tracing", "signal"] } +tokio = { workspace = true, features = [ + "time", + "rt-multi-thread", + "macros", + "tracing", + "signal", +] } tokio-rustls = { version = "0.26", default-features = false } tokio-stream = { workspace = true, features = ["time"] } -reqwest = { version = "0.12", default-features = false, features = ["cookies", "rustls-tls"] } +reqwest = { version = "0.12", default-features = false, features = [ + "cookies", + "rustls-tls", +] } secrecy = "0.8.0" sapling-crypto = "0.5.0"