• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tari-project / tari / 16215075524

11 Jul 2025 08:06AM UTC coverage: 58.066% (-0.03%) from 58.097%
16215075524

push

github

web-flow
fix: scanned height tracking (#7301)

Description
---
This should fix the display of the console wallet to again show properly
the scanned height and track the latency

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for primary and fallback HTTP server URLs for wallet
connectivity and UTXO scanning, automatically switching to a fallback if
the primary server is unavailable.
* Wallet UI now displays chain tip, scanned height, and base node
latency with improved formatting and color coding.
* Wallet state tracks and exposes synchronization heights and node
latency for enhanced status reporting.

* **Improvements**
* Wallet synchronizer and event monitor now provide detailed progress
updates, including latency and connected node information.
* Enhanced configuration options for specifying base node service
endpoints.
* Updated peer seed lists and configuration presets for improved network
connectivity.
* Base node wallet client dynamically selects between local API and
default seed server based on connectivity.

* **Bug Fixes**
* Improved handling of additional event fields for future compatibility.

* **Documentation**
* Updated configuration files to reflect new HTTP server URL options and
network seed addresses.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

0 of 140 new or added lines in 9 files covered. (0.0%)

13 existing lines in 8 files now uncovered.

69709 of 120051 relevant lines covered (58.07%)

226918.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/base_layer/wallet/src/base_node_service/monitor.rs
1
//  Copyright 2021, The Tari Project
2
//
3
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4
//  following conditions are met:
5
//
6
//  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7
//  disclaimer.
8
//
9
//  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10
//  following disclaimer in the documentation and/or other materials provided with the distribution.
11
//
12
//  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13
//  products derived from this software without specific prior written permission.
14
//
15
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16
//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20
//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21
//  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22

23
use std::{sync::Arc, time::Duration};
24

25
use chrono::Utc;
26
use log::*;
27
use minotari_node_wallet_client::BaseNodeWalletClient;
28
use tari_comms::protocol::rpc::RpcError;
29
use tari_shutdown::ShutdownSignal;
30
use tokio::{select, sync::RwLock, time::interval};
31

32
use crate::{
33
    base_node_service::{
34
        handle::{BaseNodeEvent, BaseNodeEventSender},
35
        service::BaseNodeState,
36
    },
37
    connectivity_service::WalletConnectivityInterface,
38
    error::WalletStorageError,
39
};
40

41
const LOG_TARGET: &str = "wallet::base_node_service::chain_metadata_monitor";
42

43
pub struct BaseNodeMonitor<TWalletConnectivity> {
44
    state: Arc<RwLock<BaseNodeState>>,
45
    wallet_connectivity: TWalletConnectivity,
46
    event_publisher: BaseNodeEventSender,
47
}
48

49
impl<TWalletConnectivity> BaseNodeMonitor<TWalletConnectivity>
50
where TWalletConnectivity: WalletConnectivityInterface
51
{
52
    pub fn new(
×
53
        state: Arc<RwLock<BaseNodeState>>,
×
54
        wallet_connectivity: TWalletConnectivity,
×
55
        event_publisher: BaseNodeEventSender,
×
56
    ) -> Self {
×
57
        Self {
×
58
            state,
×
59
            wallet_connectivity,
×
60
            event_publisher,
×
61
        }
×
62
    }
×
63

64
    pub async fn run(mut self, shutdown_signal: ShutdownSignal) {
×
65
        match self.monitor_node(shutdown_signal).await {
×
66
            Ok(_) => {
67
                debug!(
×
68
                    target: LOG_TARGET,
×
69
                    "Wallet Base Node Service chain metadata task completed successfully"
×
70
                );
71
            },
72

73
            Err(e @ BaseNodeMonitorError::RpcFailed(_)) => {
×
74
                warn!(target: LOG_TARGET, "Connectivity failure to base node: {}", e);
×
75
                self.update_state(BaseNodeState {
×
76
                    chain_metadata: None,
×
77
                    is_synced: None,
×
78
                    updated: None,
×
79
                    latency: None,
×
80
                })
×
81
                .await;
×
82
            },
83
            Err(e @ BaseNodeMonitorError::InvalidBaseNodeResponse(_)) |
×
84
            Err(e @ BaseNodeMonitorError::WalletStorageError(_)) => {
×
85
                error!(target: LOG_TARGET, "{}", e);
×
86
            },
87
        }
88
    }
×
89

90
    async fn monitor_node(&mut self, mut shutdown_signal: ShutdownSignal) -> Result<(), BaseNodeMonitorError> {
×
91
        let mut interval = interval(Duration::from_secs(10));
×
92
        interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
×
93

94
        loop {
95
            select! {
×
96

97
                        _ = shutdown_signal.wait() => {
×
98
                                return Ok(());
×
99
                        },
100
                        _ = interval.tick() => {
×
101
                            // continue to the next iteration
102
                    let  client = self.wallet_connectivity.obtain_base_node_wallet_rpc_client().await;
×
103

104

105

106
                    let tip_info = client
×
107
                        .get_tip_info()
×
108
                        .await
×
109
                        .map_err(|e| BaseNodeMonitorError::InvalidBaseNodeResponse(e.to_string()))?;
×
110
                    let chain_metadata = tip_info
×
111
                        .metadata
×
112
                        .ok_or_else(|| BaseNodeMonitorError::InvalidBaseNodeResponse("Tip info no metadata".to_string()))?;
×
113

NEW
114
                    let latency = match client.get_last_request_latency().await {
×
115
                        Some(latency) => latency,
×
116
                        None => {
117
                            continue;
×
118
                        },
119
                    };
120
                    debug!(
×
121
                        target: LOG_TARGET,
×
122
                        "Base node height:{} latency: {} ms",
×
123
                        chain_metadata.best_block_height(),
×
124
                        latency.as_millis()
×
125
                    );
126

127
                    let is_synced = tip_info.is_synced;
×
128
                    let best_block_height = chain_metadata.best_block_height();
×
129

×
130
                    self
×
131
                        .update_state(BaseNodeState {
×
132
                            chain_metadata: Some(chain_metadata),
×
133
                            is_synced: Some(is_synced),
×
134
                            updated: Some(Utc::now()),
×
135
                            latency: Some(latency),
×
136
                        })
×
137
                        .await;
×
138

139
                    trace!(
×
140
                        target: LOG_TARGET,
×
141
                        "Base node Tip: {} ({}) Latency: {} ms",
×
142
                        best_block_height,
×
143
                        if is_synced { "Synced" } else { "Syncing..." },
×
144
                        latency.as_millis()
×
145
                    );
146

147
               }
148
            }
149
        }
150

151
        // loop only exits on shutdown/error
152
        #[allow(unreachable_code)]
153
        Ok(())
154
    }
×
155

156
    // returns true if a new block, otherwise false
157
    async fn update_state(&self, new_state: BaseNodeState) {
×
158
        let mut lock = self.state.write().await;
×
159

160
        *lock = new_state.clone();
×
161

×
162
        self.publish_event(BaseNodeEvent::BaseNodeStateChanged(new_state));
×
163
    }
×
164

165
    fn publish_event(&self, event: BaseNodeEvent) {
×
166
        let _size = self.event_publisher.send(Arc::new(event));
×
167
    }
×
168
}
169

170
#[derive(thiserror::Error, Debug)]
171
enum BaseNodeMonitorError {
172
    #[error("Rpc error: {0}")]
173
    RpcFailed(#[from] RpcError),
174
    #[error("Invalid base node response: {0}")]
175
    InvalidBaseNodeResponse(String),
176
    #[error("Wallet storage error: {0}")]
177
    WalletStorageError(#[from] WalletStorageError),
178
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc