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

tari-project / tari / 19673252153

25 Nov 2025 02:37PM UTC coverage: 60.373% (-1.0%) from 61.332%
19673252153

push

github

web-flow
feat: improve base node check-db command (#7576)

Description
---
1. Improved the 'check-db' command to perform:
  - accumulated difficulty, with or without autocorrect;
- blockchain consistency light mode - block and header readability in
the database, with or without autocorrect;
- blockchain consistency full mode - block and header readability in the
database with block contents validation, with or without autocorrect;
- accumulated difficulty + blockchain consistency (light/full), with or
without autocorrect.

2. Added a low-level function to the lmdb_db to report on and verify
metadata keys. Corrupt non-essential keys will be auto-deleted.

The 'check-db' command runs and reports in the background, check status
persists across base node restarts and consecutive 'check-db' runs, and
'check-db' runs can be stopped at any time or restarted from the genesis
block.

```
>> check-db -h

USAGE:
    check-db [OPTIONS] --mode <MODE>

OPTIONS:
    -b, --breathing-time-ms <BREATHING_TIME_MS>
            Milli-seconds 'breathing time' between consecutive checks - very short breathing time
            may starve other critical tasks (minimum 1 ms, maximum 1000ms, default 10ms) [default:
            10]

        --do-not-clear-counters-on-error-or-stop
            Option to not clear counters when error detected or user requested a stop (Default:
            false). Note: This is a long-winded-option-to-write intended for expert use

    -h, --help
            Print help information

    -m, --mode <MODE>
            What to check [possible values: light-chain, light-autocorrect, full-chain,
            full-autocorrect, acc-diff, acc-diff-autocorrect, all-light, all-full,
            all-light-autocorrect, all-full-autocorrect, print-status, reset-counters, stop]

    -p, --poll-seconds <POLL_SECONDS>
            Seconds between status polls (default 15s) [default: 15]
```

Fixes #7575.

Motivation and Context
---

The 'check-db' command only verified that al... (continued)

106 of 1034 new or added lines in 9 files covered. (10.25%)

2045 existing lines in 41 files now uncovered.

69949 of 115862 relevant lines covered (60.37%)

689701.92 hits per line

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

91.25
/base_layer/core/src/base_node/sync/rpc/tests.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 futures::StreamExt;
24
use tari_comms::protocol::rpc::{mock::RpcRequestMock, RpcStatusCode};
25
use tari_service_framework::reply_channel;
26
use tari_test_utils::{streams::convert_mpsc_to_stream, unpack_enum};
27
use tokio::sync::broadcast;
28

29
use super::BaseNodeSyncRpcService;
30
use crate::{
31
    base_node::{BaseNodeSyncService, LocalNodeCommsInterface},
32
    chain_storage::BlockchainDatabase,
33
    proto::base_node::{SyncBlocksRequest, SyncUtxosRequest},
34
    test_helpers::{
35
        blockchain::{create_main_chain, create_new_blockchain, TempDatabase},
36
        create_peer_manager,
37
    },
38
};
39

40
fn setup() -> (
5✔
41
    BaseNodeSyncRpcService<TempDatabase>,
5✔
42
    BlockchainDatabase<TempDatabase>,
5✔
43
    RpcRequestMock,
5✔
44
) {
5✔
45
    let peer_manager = create_peer_manager();
5✔
46
    let request_mock = RpcRequestMock::new(peer_manager);
5✔
47

48
    let db = create_new_blockchain();
5✔
49
    let (req_tx, _) = reply_channel::unbounded();
5✔
50
    let (block_tx, _) = reply_channel::unbounded();
5✔
51
    let (block_event_tx, _) = broadcast::channel(1);
5✔
52
    let service = BaseNodeSyncRpcService::new(
5✔
53
        db.clone().into(),
5✔
54
        LocalNodeCommsInterface::new(req_tx, block_tx, block_event_tx),
5✔
55
    );
56
    (service, db, request_mock)
5✔
57
}
5✔
58

59
mod sync_blocks {
60
    use super::*;
61

62
    #[tokio::test]
63
    async fn it_returns_not_found_if_unknown_hash() {
1✔
64
        let (service, _, rpc_request_mock) = setup();
1✔
65
        let msg = SyncBlocksRequest {
1✔
66
            start_hash: vec![0; 32],
1✔
67
            end_hash: vec![0; 32],
1✔
68
        };
1✔
69
        let req = rpc_request_mock.request_with_context(Default::default(), msg);
1✔
70
        let err = service.sync_blocks(req).await.unwrap_err();
1✔
71
        unpack_enum!(RpcStatusCode::NotFound = err.as_status_code());
1✔
72
    }
1✔
73

74
    #[tokio::test]
75
    async fn it_sends_bad_request_on_bad_response() {
1✔
76
        let (service, db, rpc_request_mock) = setup();
1✔
77

78
        let (_, chain) = create_main_chain(&db, block_specs!(["A->GB"]));
1✔
79

80
        let block = chain.get("A").unwrap();
1✔
81
        let msg = SyncBlocksRequest {
1✔
82
            start_hash: block.hash().to_vec(),
1✔
83
            end_hash: block.hash().to_vec(),
1✔
84
        };
1✔
85
        let req = rpc_request_mock.request_with_context(Default::default(), msg);
1✔
86
        assert!(service.sync_blocks(req).await.is_err());
1✔
87
    }
1✔
88

89
    #[tokio::test]
90
    async fn it_streams_blocks_until_end() {
1✔
91
        let (service, db, rpc_request_mock) = setup();
1✔
92

93
        let (_, chain) = create_main_chain(&db, block_specs!(["A->GB"], ["B->A"], ["C->B"], ["D->C"], ["E->D"]));
1✔
94

95
        let first_block = chain.get("A").unwrap();
1✔
96
        let last_block = chain.get("E").unwrap();
1✔
97

98
        let msg = SyncBlocksRequest {
1✔
99
            start_hash: first_block.hash().to_vec(),
1✔
100
            end_hash: last_block.hash().to_vec(),
1✔
101
        };
1✔
102
        let req = rpc_request_mock.request_with_context(Default::default(), msg);
1✔
103
        let mut streaming = service.sync_blocks(req).await.unwrap().into_inner();
1✔
UNCOV
104
        let blocks = convert_mpsc_to_stream(&mut streaming)
×
UNCOV
105
            .map(|block| block.unwrap())
×
UNCOV
106
            .collect::<Vec<_>>()
×
UNCOV
107
            .await;
×
108

UNCOV
109
        assert_eq!(blocks.len(), 4);
×
UNCOV
110
        blocks.iter().zip(["B", "C", "D", "E"]).for_each(|(block, name)| {
×
111
            assert_eq!(*chain.get(name).unwrap().hash(), block.hash);
1✔
112
        });
1✔
113
    }
1✔
114
}
115

116
mod sync_utxos {
117
    use super::*;
118

119
    #[tokio::test]
120
    async fn it_returns_not_found_if_unknown_hash() {
1✔
121
        let (service, db, rpc_request_mock) = setup();
1✔
122
        let gen_block_hash = db.fetch_header(0).unwrap().unwrap().hash();
1✔
123
        let msg = SyncUtxosRequest {
1✔
124
            start_header_hash: gen_block_hash.to_vec(),
1✔
125
            end_header_hash: vec![0; 32],
1✔
126
        };
1✔
127
        let req = rpc_request_mock.request_with_context(Default::default(), msg);
1✔
128
        let err = service.sync_utxos(req).await.unwrap_err();
1✔
129
        unpack_enum!(RpcStatusCode::NotFound = err.as_status_code());
1✔
130
    }
1✔
131

132
    #[tokio::test]
133
    async fn it_returns_not_found_if_start_not_found() {
1✔
134
        let (service, db, rpc_request_mock) = setup();
1✔
135
        let (_, chain) = create_main_chain(&db, block_specs!(["A->GB"]));
1✔
136
        let gb = chain.get("GB").unwrap();
1✔
137
        let msg = SyncUtxosRequest {
1✔
138
            start_header_hash: vec![0; 32],
1✔
139
            end_header_hash: gb.hash().to_vec(),
1✔
140
        };
1✔
141
        let req = rpc_request_mock.request_with_context(Default::default(), msg);
1✔
142
        let err = service.sync_utxos(req).await.unwrap_err();
1✔
UNCOV
143
        unpack_enum!(RpcStatusCode::NotFound = err.as_status_code());
×
144
    }
1✔
145
}
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