drive_abci/verify/
mod.rs

1use crate::config::PlatformConfig;
2use crate::platform_types::platform::Platform;
3use crate::platform_types::platform_state::{PlatformState, PlatformStateV0Methods};
4use crate::rpc::core::DefaultCoreRPC;
5use dpp::version::PlatformVersion;
6use drive::drive::Drive;
7use std::fs::remove_file;
8use std::path::PathBuf;
9
10/// Run all verification steps:
11/// - GroveDB integrity
12/// - platform state app hash vs GroveDB root
13/// - configuration consistency with stored protocol version
14pub fn run(config: &PlatformConfig, force: bool) -> Result<(), String> {
15    verify_grovedb(&config.db_path, force)?;
16
17    let (drive, maybe_platform_version) =
18        Drive::open(&config.db_path, Some(config.drive.clone())).map_err(|e| e.to_string())?;
19
20    let platform_version = maybe_platform_version.unwrap_or_else(PlatformVersion::latest);
21
22    let platform_state =
23        Platform::<DefaultCoreRPC>::fetch_platform_state(&drive, None, platform_version)
24            .map_err(|e| e.to_string())?
25            .ok_or_else(|| "platform state missing from database".to_string())?;
26
27    verify_app_hash_matches_grove_root(&drive, &platform_state, platform_version)?;
28    verify_config_matches_db(config, &drive)?;
29
30    Ok(())
31}
32
33/// Verify GroveDB integrity.
34///
35/// This function will execute GroveDB integrity checks if one of the following conditions is met:
36/// - `force` is `true`
37/// - file `.fsck` in `db_path` exists
38///
39/// After successful verification, .fsck file is removed.
40pub fn verify_grovedb(db_path: &PathBuf, force: bool) -> Result<(), String> {
41    let fsck = PathBuf::from(db_path).join(".fsck");
42
43    if !force {
44        if !fsck.exists() {
45            return Ok(());
46        }
47        tracing::info!(
48            "found {} file, starting grovedb verification",
49            fsck.display()
50        );
51    }
52
53    let grovedb = drive::grovedb::GroveDb::open(db_path)
54        .map_err(|e| format!("failed to open grovedb: {}", e))?;
55    // TODO: fetch platform version instead of taking latest
56    let result = grovedb
57        .visualize_verify_grovedb(
58            None,
59            true,
60            true,
61            &PlatformVersion::latest().drive.grove_version,
62        )
63        .map_err(|e| e.to_string());
64
65    match result {
66        Ok(data) => {
67            for result in &data {
68                tracing::warn!(?result, "grovedb verification")
69            }
70            tracing::info!("grovedb verification finished");
71
72            if fsck.exists() {
73                if let Err(e) = remove_file(&fsck) {
74                    tracing::warn!(
75                        error = ?e,
76                        path = fsck.display().to_string(),
77                        "grovedb verification: cannot remove .fsck file: please remove it manually to avoid running verification again",
78                    );
79                }
80            }
81            if data.is_empty() {
82                Ok(())
83            } else {
84                Err(format!(
85                    "grovedb verification found {} issue(s)",
86                    data.len()
87                ))
88            }
89        }
90        Err(e) => {
91            tracing::error!("grovedb verification failed: {}", e);
92            Err(e)
93        }
94    }
95}
96
97fn verify_app_hash_matches_grove_root(
98    drive: &Drive,
99    platform_state: &PlatformState,
100    platform_version: &PlatformVersion,
101) -> Result<(), String> {
102    let app_hash = platform_state
103        .last_committed_block_app_hash()
104        .ok_or_else(|| "platform state missing last committed app hash".to_string())?;
105
106    let grove_root = drive
107        .grove
108        .root_hash(None, &platform_version.drive.grove_version)
109        .unwrap()
110        .map_err(|e| e.to_string())?;
111
112    if grove_root != app_hash {
113        return Err(format!(
114            "app hash mismatch: platform state 0x{} vs grovedb root 0x{}",
115            hex::encode(app_hash),
116            hex::encode(grove_root)
117        ));
118    }
119
120    tracing::info!("platform state app hash matches grovedb root hash");
121    Ok(())
122}
123
124fn verify_config_matches_db(_config: &PlatformConfig, drive: &Drive) -> Result<(), String> {
125    let stored_protocol_version = drive
126        .fetch_current_protocol_version(None)
127        .map_err(|e| e.to_string())?;
128
129    if let Some(stored) = stored_protocol_version {
130        let binary_supported = PlatformVersion::latest().protocol_version;
131        if stored > binary_supported {
132            return Err(format!(
133                "database protocol version {} is newer than binary supports {}",
134                stored, binary_supported
135            ));
136        }
137        tracing::info!(
138            "protocol version in DB {} is compatible with binary {}",
139            stored,
140            binary_supported
141        );
142    } else {
143        tracing::warn!("no protocol version found in DB to compare with binary support");
144    }
145
146    Ok(())
147}