blocks.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use log::info;
  19. use rusqlite::types::Value;
  20. use tinyjson::JsonValue;
  21. use darkfi::{blockchain::BlockInfo, Error, Result};
  22. use darkfi_sdk::crypto::schnorr::Signature;
  23. use darkfi_serial::{deserialize, serialize};
  24. use drk::{
  25. convert_named_params,
  26. error::{WalletDbError, WalletDbResult},
  27. };
  28. use crate::BlockchainExplorer;
  29. // Database SQL table constant names. These have to represent the `blocks.sql`
  30. // SQL schema.
  31. pub const BLOCKS_TABLE: &str = "blocks";
  32. // BLOCKS_TABLE
  33. pub const BLOCKS_COL_HEADER_HASH: &str = "header_hash";
  34. pub const BLOCKS_COL_VERSION: &str = "version";
  35. pub const BLOCKS_COL_PREVIOUS: &str = "previous";
  36. pub const BLOCKS_COL_HEIGHT: &str = "height";
  37. pub const BLOCKS_COL_TIMESTAMP: &str = "timestamp";
  38. pub const BLOCKS_COL_NONCE: &str = "nonce";
  39. pub const BLOCKS_COL_ROOT: &str = "root";
  40. pub const BLOCKS_COL_SIGNATURE: &str = "signature";
  41. #[derive(Debug, Clone)]
  42. /// Structure representing a `BLOCKS_TABLE` record.
  43. pub struct BlockRecord {
  44. /// Header hash identifier of the block
  45. pub header_hash: String,
  46. /// Block version
  47. pub version: u8,
  48. /// Previous block hash
  49. pub previous: String,
  50. /// Block height
  51. pub height: u32,
  52. /// Block creation timestamp
  53. pub timestamp: u64,
  54. /// The block's nonce. This value changes arbitrarily with mining.
  55. pub nonce: u64,
  56. /// Merkle tree root of the transactions hashes contained in this block
  57. pub root: String,
  58. /// Block producer signature
  59. pub signature: Signature,
  60. }
  61. impl BlockRecord {
  62. /// Auxiliary function to convert a `BlockRecord` into a `JsonValue` array.
  63. pub fn to_json_array(&self) -> JsonValue {
  64. JsonValue::Array(vec![
  65. JsonValue::String(self.header_hash.clone()),
  66. JsonValue::Number(self.version as f64),
  67. JsonValue::String(self.previous.clone()),
  68. JsonValue::Number(self.height as f64),
  69. JsonValue::Number(self.timestamp as f64),
  70. JsonValue::Number(self.nonce as f64),
  71. JsonValue::String(self.root.clone()),
  72. JsonValue::String(format!("{:?}", self.signature)),
  73. ])
  74. }
  75. }
  76. impl From<&BlockInfo> for BlockRecord {
  77. fn from(block: &BlockInfo) -> Self {
  78. Self {
  79. header_hash: block.hash().to_string(),
  80. version: block.header.version,
  81. previous: block.header.previous.to_string(),
  82. height: block.header.height,
  83. timestamp: block.header.timestamp.inner(),
  84. nonce: block.header.nonce,
  85. root: block.header.root.to_string(),
  86. signature: block.signature,
  87. }
  88. }
  89. }
  90. impl BlockchainExplorer {
  91. /// Initialize database with blocks tables.
  92. pub async fn initialize_blocks(&self) -> WalletDbResult<()> {
  93. // Initialize blocks database schema
  94. let database_schema = include_str!("../blocks.sql");
  95. self.database.exec_batch_sql(database_schema)?;
  96. Ok(())
  97. }
  98. /// Reset blocks table in the database.
  99. pub fn reset_blocks(&self) -> WalletDbResult<()> {
  100. info!(target: "blockchain-explorer::blocks::reset_blocks", "Resetting blocks...");
  101. let query = format!("DELETE FROM {};", BLOCKS_TABLE);
  102. self.database.exec_sql(&query, &[])
  103. }
  104. /// Import given block into the database.
  105. pub async fn put_block(&self, block: &BlockRecord) -> Result<()> {
  106. let query = format!(
  107. "INSERT OR REPLACE INTO {} ({}, {}, {}, {}, {}, {}, {}, {}) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);",
  108. BLOCKS_TABLE,
  109. BLOCKS_COL_HEADER_HASH,
  110. BLOCKS_COL_VERSION,
  111. BLOCKS_COL_PREVIOUS,
  112. BLOCKS_COL_HEIGHT,
  113. BLOCKS_COL_TIMESTAMP,
  114. BLOCKS_COL_NONCE,
  115. BLOCKS_COL_ROOT,
  116. BLOCKS_COL_SIGNATURE
  117. );
  118. if let Err(e) = self.database.exec_sql(
  119. &query,
  120. rusqlite::params![
  121. block.header_hash,
  122. block.version,
  123. block.previous,
  124. block.height,
  125. block.timestamp,
  126. block.nonce,
  127. block.root,
  128. serialize(&block.signature),
  129. ],
  130. ) {
  131. return Err(Error::RusqliteError(format!("[put_block] Block insert failed: {e:?}")))
  132. };
  133. Ok(())
  134. }
  135. /// Auxiliary function to parse a `BLOCKS_TABLE` record.
  136. fn parse_block_record(&self, row: &[Value]) -> Result<BlockRecord> {
  137. let Value::Text(ref header_hash) = row[0] else {
  138. return Err(Error::ParseFailed("[parse_block_record] Header hash parsing failed"))
  139. };
  140. let header_hash = header_hash.clone();
  141. let Value::Integer(version) = row[1] else {
  142. return Err(Error::ParseFailed("[parse_block_record] Version parsing failed"))
  143. };
  144. let Ok(version) = u8::try_from(version) else {
  145. return Err(Error::ParseFailed("[parse_block_record] Version parsing failed"))
  146. };
  147. let Value::Text(ref previous) = row[2] else {
  148. return Err(Error::ParseFailed("[parse_block_record] Previous parsing failed"))
  149. };
  150. let previous = previous.clone();
  151. let Value::Integer(height) = row[3] else {
  152. return Err(Error::ParseFailed("[parse_block_record] Height parsing failed"))
  153. };
  154. let Ok(height) = u32::try_from(height) else {
  155. return Err(Error::ParseFailed("[parse_block_record] Height parsing failed"))
  156. };
  157. let Value::Integer(timestamp) = row[4] else {
  158. return Err(Error::ParseFailed("[parse_block_record] Timestamp parsing failed"))
  159. };
  160. let Ok(timestamp) = u64::try_from(timestamp) else {
  161. return Err(Error::ParseFailed("[parse_block_record] Timestamp parsing failed"))
  162. };
  163. let Value::Integer(nonce) = row[5] else {
  164. return Err(Error::ParseFailed("[parse_block_record] Nonce parsing failed"))
  165. };
  166. let Ok(nonce) = u64::try_from(nonce) else {
  167. return Err(Error::ParseFailed("[parse_block_record] Nonce parsing failed"))
  168. };
  169. let Value::Text(ref root) = row[6] else {
  170. return Err(Error::ParseFailed("[parse_block_record] Root parsing failed"))
  171. };
  172. let root = root.clone();
  173. let Value::Blob(ref signature_bytes) = row[7] else {
  174. return Err(Error::ParseFailed(
  175. "[parse_block_record] Signature bytes bytes parsing failed",
  176. ))
  177. };
  178. let signature = deserialize(signature_bytes)?;
  179. Ok(BlockRecord {
  180. header_hash,
  181. version,
  182. previous,
  183. height,
  184. timestamp,
  185. nonce,
  186. root,
  187. signature,
  188. })
  189. }
  190. /// Fetch all known blocks from the database.
  191. pub fn get_blocks(&self) -> Result<Vec<BlockRecord>> {
  192. let rows = match self.database.query_multiple(BLOCKS_TABLE, &[], &[]) {
  193. Ok(r) => r,
  194. Err(e) => {
  195. return Err(Error::RusqliteError(format!(
  196. "[get_blocks] Blocks retrieval failed: {e:?}"
  197. )))
  198. }
  199. };
  200. let mut blocks = Vec::with_capacity(rows.len());
  201. for row in rows {
  202. blocks.push(self.parse_block_record(&row)?);
  203. }
  204. Ok(blocks)
  205. }
  206. /// Fetch a block given its header hash.
  207. pub fn get_block_by_hash(&self, header_hash: &str) -> Result<BlockRecord> {
  208. let row = match self.database.query_single(
  209. BLOCKS_TABLE,
  210. &[],
  211. convert_named_params! {(BLOCKS_COL_HEADER_HASH, header_hash)},
  212. ) {
  213. Ok(r) => r,
  214. Err(e) => {
  215. return Err(Error::RusqliteError(format!(
  216. "[get_block_by_hash] Block retrieval failed: {e:?}"
  217. )))
  218. }
  219. };
  220. self.parse_block_record(&row)
  221. }
  222. /// Fetch last block height from the database.
  223. pub async fn last_block(&self) -> WalletDbResult<(u32, String)> {
  224. // First we prepare the query
  225. let query = format!(
  226. "SELECT {}, {} FROM {} ORDER BY {} DESC LIMIT 1;",
  227. BLOCKS_COL_HEADER_HASH, BLOCKS_COL_HEIGHT, BLOCKS_TABLE, BLOCKS_COL_HEIGHT
  228. );
  229. let Ok(conn) = self.database.conn.lock() else {
  230. return Err(WalletDbError::FailedToAquireLock)
  231. };
  232. let Ok(mut stmt) = conn.prepare(&query) else {
  233. return Err(WalletDbError::QueryPreparationFailed)
  234. };
  235. // Execute the query using provided params
  236. let Ok(mut rows) = stmt.query([]) else { return Err(WalletDbError::QueryExecutionFailed) };
  237. // Check if row exists
  238. let Ok(next) = rows.next() else { return Err(WalletDbError::QueryExecutionFailed) };
  239. let row = match next {
  240. Some(row_result) => row_result,
  241. None => return Ok((0_u32, "".to_string())),
  242. };
  243. // Parse returned values
  244. let Ok(value) = row.get(0) else { return Err(WalletDbError::ParseColumnValueError) };
  245. let Value::Text(ref header_hash) = value else {
  246. return Err(WalletDbError::ParseColumnValueError)
  247. };
  248. let header_hash = header_hash.clone();
  249. let Ok(value) = row.get(1) else { return Err(WalletDbError::ParseColumnValueError) };
  250. let Value::Integer(height) = value else {
  251. return Err(WalletDbError::ParseColumnValueError)
  252. };
  253. let Ok(height) = u32::try_from(height) else {
  254. return Err(WalletDbError::ParseColumnValueError)
  255. };
  256. Ok((height, header_hash))
  257. }
  258. /// Auxiliary function to parse a `BLOCKS_TABLE` query rows into block records.
  259. fn parse_blocks_query_rows(&self, rows: &mut rusqlite::Rows) -> Result<Vec<BlockRecord>> {
  260. // Loop over returned rows and parse them
  261. let mut records = vec![];
  262. loop {
  263. // Check if an error occured
  264. let row = match rows.next() {
  265. Ok(r) => r,
  266. Err(_) => {
  267. return Err(Error::RusqliteError(format!(
  268. "[get_last_n_blocks] {}",
  269. WalletDbError::QueryExecutionFailed
  270. )))
  271. }
  272. };
  273. // Check if no row was returned
  274. let row = match row {
  275. Some(r) => r,
  276. None => break,
  277. };
  278. // Grab row returned values
  279. let mut row_values = vec![];
  280. let mut idx = 0;
  281. loop {
  282. let Ok(value) = row.get(idx) else { break };
  283. row_values.push(value);
  284. idx += 1;
  285. }
  286. records.push(row_values);
  287. }
  288. // Parse the records into blocks
  289. let mut blocks = Vec::with_capacity(records.len());
  290. for record in records {
  291. blocks.push(self.parse_block_record(&record)?);
  292. }
  293. Ok(blocks)
  294. }
  295. /// Fetch last N blocks from the database.
  296. pub fn get_last_n_blocks(&self, n: u16) -> Result<Vec<BlockRecord>> {
  297. // First we prepare the query
  298. let query = format!(
  299. "SELECT * FROM {} ORDER BY {} DESC LIMIT {};",
  300. BLOCKS_TABLE, BLOCKS_COL_HEIGHT, n
  301. );
  302. let Ok(conn) = self.database.conn.lock() else {
  303. return Err(Error::RusqliteError(format!(
  304. "[get_last_n_blocks] {}",
  305. WalletDbError::FailedToAquireLock
  306. )))
  307. };
  308. let Ok(mut stmt) = conn.prepare(&query) else {
  309. return Err(Error::RusqliteError(format!(
  310. "[get_last_n_blocks] {}",
  311. WalletDbError::QueryPreparationFailed
  312. )))
  313. };
  314. // Execute the query using provided params
  315. let Ok(mut rows) = stmt.query([]) else {
  316. return Err(Error::RusqliteError(format!(
  317. "[get_last_n_blocks] {}",
  318. WalletDbError::QueryExecutionFailed
  319. )))
  320. };
  321. self.parse_blocks_query_rows(&mut rows)
  322. }
  323. /// Fetch last N blocks from the database.
  324. pub fn get_blocks_in_heights_range(&self, start: u32, end: u32) -> Result<Vec<BlockRecord>> {
  325. // First we prepare the query
  326. let query = format!(
  327. "SELECT * FROM {} WHERE {} >= {} AND {} <= {} ORDER BY {} ASC;",
  328. BLOCKS_TABLE, BLOCKS_COL_HEIGHT, start, BLOCKS_COL_HEIGHT, end, BLOCKS_COL_HEIGHT
  329. );
  330. let Ok(conn) = self.database.conn.lock() else {
  331. return Err(Error::RusqliteError(format!(
  332. "[get_blocks_in_height_range] {}",
  333. WalletDbError::FailedToAquireLock
  334. )))
  335. };
  336. let Ok(mut stmt) = conn.prepare(&query) else {
  337. return Err(Error::RusqliteError(format!(
  338. "[get_blocks_in_height_range] {}",
  339. WalletDbError::QueryPreparationFailed
  340. )))
  341. };
  342. // Execute the query using provided params
  343. let Ok(mut rows) = stmt.query([]) else {
  344. return Err(Error::RusqliteError(format!(
  345. "[get_blocks_in_height_range] {}",
  346. WalletDbError::QueryExecutionFailed
  347. )))
  348. };
  349. self.parse_blocks_query_rows(&mut rows)
  350. }
  351. }