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

tari-project / tari / 26223687530

21 May 2026 11:40AM UTC coverage: 60.422% (-0.02%) from 60.442%
26223687530

push

github

web-flow
feat(xmrig-proxy): add getinfo and getheight methods to the node's integrated xmrig_proxy (#7827)

Description
---
Some third-party applications request getinfo and/or getheight

This PR adds dedicated handlers for getinfo and getheight while
preserving compatibility with clients which may be calling get_height or
getblockcount

Modifications:
- Implemented handle_get_height_hash() and handle_get_info() with
explicit routing
- Extracted shared chain tip into get_chain_tip() for use in all
handlers
- Registered corresponding GET paths (/getheight, /getinfo, /get_info)
- Added Cucumber integration tests validating all endpoints and dynamic
height updates

These additions improve miner interoperability without altering the
existing proxy functionality. Redundant node service calls are
consolidated

Motivation and Context
---
Mining and mining pool software often expects a get_info/getinfo
endpoint and may use getheight instead of get_height. By giving
getheight its own handler rather than merging it with get_height, this
PR:

- Preserves compatibility — existing getblockcount/get_height clients
see no change
- Provides enriched data to getheight callers (block hash alongside
height)
- Adds compatible get_info and getinfo for applications expecting them

How Has This Been Tested?
---
Cucumber Test Coverage: Test Scenarios (6 total)
 **| Scenario                     | Method | Endpoint | Validation**

1 | JSON-RPC getheight | POST | getheight | Response contains height,
id, status
2 | JSON-RPC getinfo | POST | getinfo | Response contains height, id,
status, credits
3 | GET /getheight | GET | /getheight | Response contains height, id,
status
4 | GET /getinfo | GET | /getinfo | Response contains height, id,
status, credits
5 | JSON-RPC getheight reflects mined blocks | POST + mining | getheight
| Height matches base node after mining 3 blocks
6 | GET /getheight reflects mined blocks | GET + mining | /getheight |
Height matches base node after mining 3 blocks... (continued)

70473 of 116635 relevant lines covered (60.42%)

222750.16 hits per line

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

28.0
/base_layer/core/src/base_node/sync/header_sync/error.rs
1
//  Copyright 2020, 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::time::Duration;
24

25
use primitive_types::U512;
26
use tari_comms::{
27
    connectivity::ConnectivityError,
28
    peer_manager::NodeId,
29
    protocol::rpc::{RpcError, RpcStatus},
30
};
31
use tari_node_components::blocks::BlockError;
32
use tari_transaction_components::{BanPeriod, BanReason};
33

34
use crate::{chain_storage::ChainStorageError, validation::ValidationError};
35

36
#[derive(Debug, thiserror::Error)]
37
pub enum BlockHeaderSyncError {
38
    #[error("No more sync peers available: {0}")]
39
    NoMoreSyncPeers(String),
40
    #[error("Could not find peer info")]
41
    PeerNotFound,
42
    #[error("RPC error: {0}")]
43
    RpcError(#[from] RpcError),
44
    #[error("RPC request failed: {0}")]
45
    RpcRequestError(#[from] RpcStatus),
46
    #[error("Peer sent invalid header: {0}")]
47
    ReceivedInvalidHeader(String),
48
    #[error("Chain storage error: {0}")]
49
    ChainStorageError(#[from] ChainStorageError),
50
    #[error("Validation failed: {0}")]
51
    ValidationFailed(#[from] ValidationError),
52
    #[error("Sync failed for all peers")]
53
    SyncFailedAllPeers,
54
    #[error("Peer sent a found hash index that was out of range (Expected less than {0}, Found: {1})")]
55
    FoundHashIndexOutOfRange(u64, u64),
56
    #[error("Failed to ban peer: {0}")]
57
    FailedToBan(ConnectivityError),
58
    #[error("Connectivity Error: {0}")]
59
    ConnectivityError(#[from] ConnectivityError),
60
    #[error("Node is still not in sync. Sync will be retried with another peer if possible.")]
61
    NotInSync,
62
    #[error("Unable to locate start hash `{0}`")]
63
    StartHashNotFound(String),
64
    #[error("Expected header height {expected} got {actual}")]
65
    InvalidBlockHeight { expected: u64, actual: u64 },
66
    #[error("Unable to find chain split from peer `{0}`")]
67
    ChainSplitNotFound(NodeId),
68
    #[error("Invalid protocol response: {0}")]
69
    InvalidProtocolResponse(String),
70
    #[error("Header at height {height} did not form a chain. Expected {actual} to equal the previous hash {expected}")]
71
    ChainLinkBroken {
72
        height: u64,
73
        actual: String,
74
        expected: String,
75
    },
76
    #[error("Block error: {0}")]
77
    BlockError(#[from] BlockError),
78
    #[error(
79
        "Peer claimed a stronger chain than they were able to provide. Claimed {claimed}, Actual: {actual:?}, local: \
80
         {local}"
81
    )]
82
    PeerSentInaccurateChainMetadata {
83
        claimed: U512,
84
        actual: Option<U512>,
85
        local: U512,
86
    },
87
    #[error("This peer sent too many headers ({0}) in response to a chain split request")]
88
    PeerSentTooManyHeaders(usize),
89
    #[error("Peer {peer} exceeded maximum permitted sync latency. latency: {latency:.2?}s, max: {max_latency:.2?}s")]
90
    MaxLatencyExceeded {
91
        peer: NodeId,
92
        latency: Duration,
93
        max_latency: Duration,
94
    },
95
    #[error("All sync peers exceeded max allowed latency")]
96
    AllSyncPeersExceedLatency,
97
    #[error("Unable to get TargetDifficulties: ({0})")]
98
    TargetDifficultiesError(String),
99
}
100

101
impl BlockHeaderSyncError {
102
    pub fn get_ban_reason(&self) -> Option<BanReason> {
1✔
103
        match self {
1✔
104
            // no ban
105
            BlockHeaderSyncError::NoMoreSyncPeers(_) |
106
            BlockHeaderSyncError::SyncFailedAllPeers |
107
            BlockHeaderSyncError::FailedToBan(_) |
108
            BlockHeaderSyncError::AllSyncPeersExceedLatency |
109
            BlockHeaderSyncError::ConnectivityError(_) |
110
            BlockHeaderSyncError::NotInSync |
111
            BlockHeaderSyncError::TargetDifficultiesError(_) |
112
            BlockHeaderSyncError::PeerNotFound => None,
×
113
            BlockHeaderSyncError::ChainStorageError(e) => e.get_ban_reason(),
×
114

115
            // short ban
116
            err @ BlockHeaderSyncError::MaxLatencyExceeded { .. } |
×
117
            err @ BlockHeaderSyncError::RpcError { .. } |
×
118
            err @ BlockHeaderSyncError::RpcRequestError { .. } |
×
119
            err @ BlockHeaderSyncError::PeerSentInaccurateChainMetadata { .. } => Some(BanReason {
1✔
120
                reason: format!("{err}"),
1✔
121
                ban_duration: BanPeriod::Short,
1✔
122
            }),
1✔
123

124
            // long ban
125
            err @ BlockHeaderSyncError::ReceivedInvalidHeader(_) |
×
126
            err @ BlockHeaderSyncError::FoundHashIndexOutOfRange(_, _) |
×
127
            err @ BlockHeaderSyncError::StartHashNotFound(_) |
×
128
            err @ BlockHeaderSyncError::InvalidBlockHeight { .. } |
×
129
            err @ BlockHeaderSyncError::ChainSplitNotFound(_) |
×
130
            err @ BlockHeaderSyncError::InvalidProtocolResponse(_) |
×
131
            err @ BlockHeaderSyncError::ChainLinkBroken { .. } |
×
132
            err @ BlockHeaderSyncError::BlockError(_) |
×
133
            err @ BlockHeaderSyncError::PeerSentTooManyHeaders(_) => Some(BanReason {
×
134
                reason: format!("{err}"),
×
135
                ban_duration: BanPeriod::Long,
×
136
            }),
×
137

138
            BlockHeaderSyncError::ValidationFailed(err) => ValidationError::get_ban_reason(err),
×
139
        }
140
    }
1✔
141
}
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