Skip to content

Commit a5c50dd

Browse files
committed
introduce BlockHeaderStorage.get_tip helper
Introduces `get_tip() -> Option<BlockHeaderTip>` in `BlockHeaderStorage`. This consolidates two-step pattern of `get_tip_height()` followed by `get_header(tip_height)` into a single `get_tip()` call. I didn't adjust any of the other places (except for one) where it's currently possible to be used since they are in the "old" sync code and this PR here is more like a preparation to be used by the sync rewrite.
1 parent 9f1a9b4 commit a5c50dd

File tree

3 files changed

+73
-10
lines changed

3 files changed

+73
-10
lines changed

dash-spv/src/client/core.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,7 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
184184
/// Returns the current chain tip hash if available.
185185
pub async fn tip_hash(&self) -> Option<dashcore::BlockHash> {
186186
let storage = self.storage.lock().await;
187-
188-
let tip_height = storage.get_tip_height().await?;
189-
let header = storage.get_header(tip_height).await.ok()??;
190-
191-
Some(header.block_hash())
187+
storage.get_tip().await.map(|tip| *tip.hash())
192188
}
193189

194190
/// Returns the current chain tip height (absolute), accounting for checkpoint base.

dash-spv/src/storage/blocks.rs

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,41 @@ use std::collections::HashMap;
44
use std::ops::Range;
55
use std::path::PathBuf;
66

7+
use crate::error::StorageResult;
8+
use crate::storage::segments::SegmentCache;
9+
use crate::storage::PersistentStorage;
10+
use crate::types::HashedBlockHeader;
711
use async_trait::async_trait;
812
use dashcore::block::Header as BlockHeader;
13+
use dashcore::prelude::CoreBlockHeight;
914
use dashcore::BlockHash;
1015
use tokio::sync::RwLock;
1116

12-
use crate::error::StorageResult;
13-
use crate::storage::segments::SegmentCache;
14-
use crate::storage::PersistentStorage;
15-
use crate::types::HashedBlockHeader;
17+
#[derive(Debug, PartialEq)]
18+
pub struct BlockHeaderTip {
19+
height: CoreBlockHeight,
20+
header: BlockHeader,
21+
hash: BlockHash,
22+
}
23+
24+
impl BlockHeaderTip {
25+
pub fn new(height: CoreBlockHeight, hashed_block_header: HashedBlockHeader) -> Self {
26+
Self {
27+
height,
28+
header: *hashed_block_header.header(),
29+
hash: *hashed_block_header.hash(),
30+
}
31+
}
32+
pub fn height(&self) -> CoreBlockHeight {
33+
self.height
34+
}
35+
pub fn header(&self) -> &BlockHeader {
36+
&self.header
37+
}
38+
pub fn hash(&self) -> &BlockHash {
39+
&self.hash
40+
}
41+
}
1642

1743
#[async_trait]
1844
pub trait BlockHeaderStorage {
@@ -48,6 +74,8 @@ pub trait BlockHeaderStorage {
4874

4975
async fn get_tip_height(&self) -> Option<u32>;
5076

77+
async fn get_tip(&self) -> Option<BlockHeaderTip>;
78+
5179
async fn get_start_height(&self) -> Option<u32>;
5280

5381
async fn get_stored_headers_len(&self) -> u32;
@@ -146,6 +174,14 @@ impl BlockHeaderStorage for PersistentBlockHeaderStorage {
146174
self.block_headers.read().await.tip_height()
147175
}
148176

177+
async fn get_tip(&self) -> Option<BlockHeaderTip> {
178+
let mut block_headers = self.block_headers.write().await;
179+
let tip_height = block_headers.tip_height()?;
180+
let hashed_header =
181+
block_headers.get_items(tip_height..tip_height + 1).await.ok()?.into_iter().next()?;
182+
Some(BlockHeaderTip::new(tip_height, hashed_header))
183+
}
184+
149185
async fn get_start_height(&self) -> Option<u32> {
150186
self.block_headers.read().await.start_height()
151187
}
@@ -175,3 +211,30 @@ impl BlockHeaderStorage for PersistentBlockHeaderStorage {
175211
Ok(self.header_hash_index.get(hash).copied())
176212
}
177213
}
214+
215+
#[cfg(test)]
216+
mod tests {
217+
use super::*;
218+
use tempfile::TempDir;
219+
220+
#[tokio::test]
221+
async fn test_get_tip() {
222+
let headers = BlockHeader::dummy_batch(0..5);
223+
let tmp_dir = TempDir::new().unwrap();
224+
let mut storage = PersistentBlockHeaderStorage::open(tmp_dir.path()).await.unwrap();
225+
// Tip should be none before storing headers
226+
assert!(storage.get_tip().await.is_none());
227+
// Add one header and validate tip
228+
storage.store_headers(&headers[0..1]).await.unwrap();
229+
let tip = storage.get_tip().await.unwrap();
230+
let expected_tip = BlockHeaderTip::new(0, HashedBlockHeader::from(headers[0]));
231+
assert_eq!(tip, expected_tip);
232+
assert_eq!(storage.get_tip_height().await, Some(0));
233+
// Add multiple headers and validate tip
234+
storage.store_headers(&headers[1..]).await.unwrap();
235+
let tip = storage.get_tip().await.unwrap();
236+
let expected_tip = BlockHeaderTip::new(4, HashedBlockHeader::from(headers[4]));
237+
assert_eq!(tip, expected_tip);
238+
assert_eq!(storage.get_tip_height().await, Some(4));
239+
}
240+
}

dash-spv/src/storage/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::time::Duration;
2424
use tokio::sync::RwLock;
2525

2626
use crate::error::StorageResult;
27-
use crate::storage::blocks::PersistentBlockHeaderStorage;
27+
use crate::storage::blocks::{BlockHeaderTip, PersistentBlockHeaderStorage};
2828
use crate::storage::chainstate::PersistentChainStateStorage;
2929
use crate::storage::filters::{PersistentFilterHeaderStorage, PersistentFilterStorage};
3030
use crate::storage::lockfile::LockFile;
@@ -273,6 +273,10 @@ impl blocks::BlockHeaderStorage for DiskStorageManager {
273273
self.block_headers.read().await.get_tip_height().await
274274
}
275275

276+
async fn get_tip(&self) -> Option<BlockHeaderTip> {
277+
self.block_headers.read().await.get_tip().await
278+
}
279+
276280
async fn get_start_height(&self) -> Option<u32> {
277281
self.block_headers.read().await.get_start_height().await
278282
}

0 commit comments

Comments
 (0)