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
10pub 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
33pub 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 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}