-
Notifications
You must be signed in to change notification settings - Fork 26
feat: serve chain from store to followers #537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5d6c515
8f5a4d4
151882e
3eff1cc
842971f
5c5305e
3779ec2
bffea1a
b102f4c
9a770b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -250,6 +250,25 @@ macro_rules! impl_ReadOnlyChainStore { | |
| }) | ||
| } | ||
|
|
||
| fn next_best_chain(&self, point: &Point) -> Option<Point> { | ||
| let readopts = ReadOptions::default(); | ||
| let prefix = [&CHAIN_PREFIX[..], &(u64::from(point.slot_or_default()) + 1).to_be_bytes()].concat(); | ||
| let mut iter = self.db.iterator_opt(IteratorMode::From(&prefix, rocksdb::Direction::Forward), readopts); | ||
|
|
||
| if let Some(Ok((k, v))) = iter.next() { | ||
| let slot_bytes = &k[CHAIN_PREFIX.len()..CHAIN_PREFIX.len() + 8]; | ||
| let slot = u64::from_be_bytes(slot_bytes.try_into().unwrap()); | ||
| if v.len() == HEADER_HASH_SIZE { | ||
| let hash = HeaderHash::from(v.as_ref()); | ||
| Some(Point::Specific(slot, hash.to_vec())) | ||
| } else { | ||
| None | ||
| } | ||
| } else { | ||
| None | ||
| } | ||
| } | ||
|
Comment on lines
+253
to
+270
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implementation looks solid, but there's a potential panic lurking. The logic is straightforward - start iterating from slot+1, grab the first result, parse it, done. However, line 260 has an let slot = u64::from_be_bytes(slot_bytes.try_into().unwrap());If the database is corrupted or the key format somehow changes, this'll bring the whole node down faster than you can say "crikey". Consider replacing with: - let slot = u64::from_be_bytes(slot_bytes.try_into().unwrap());
+ let slot_array: [u8; 8] = match slot_bytes.try_into() {
+ Ok(arr) => arr,
+ Err(_) => {
+ tracing::error!("Invalid slot bytes length in chain store");
+ return None;
+ }
+ };
+ let slot = u64::from_be_bytes(slot_array);Or at minimum use Also, minor observation: the implementation relies on the iterator's prefix filtering to ensure keys start with CHAIN_PREFIX. This is fine, but if you're being defensive you could add an explicit check - though the iterator prefix mode should handle this already. |
||
|
|
||
| })* | ||
| } | ||
| } | ||
|
|
@@ -729,6 +748,53 @@ pub mod test { | |
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn next_best_chain_returns_successor_give_valid_point() { | ||
| with_db(|store| { | ||
| let chain = populate_db(store.clone()); | ||
|
|
||
| let result = store | ||
| .next_best_chain(&chain[5].point()) | ||
| .expect("should find successor"); | ||
|
|
||
| assert_eq!(result, chain[6].point()); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn next_best_chain_returns_first_point_on_chain_given_origin() { | ||
| with_db(|store| { | ||
| let chain = populate_db(store.clone()); | ||
|
|
||
| let result = store | ||
| .next_best_chain(&Point::Origin) | ||
| .expect("should find successor"); | ||
|
|
||
| assert_eq!(result, chain[0].point()); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn next_best_chain_returns_none_given_point_is_not_on_chain() { | ||
| with_db(|store| { | ||
| let _chain = populate_db(store.clone()); | ||
| let invalid_point = Point::Specific(100, random_hash().to_vec()); | ||
|
|
||
| assert!(store.next_best_chain(&invalid_point).is_none()); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn next_best_chain_returns_none_given_point_is_tip() { | ||
| with_db(|store| { | ||
| let _chain = populate_db(store.clone()); | ||
| let tip = store.get_best_chain_hash(); | ||
| let tip_header = store.load_header(&tip).unwrap(); | ||
|
|
||
| assert!(store.next_best_chain(&tip_header.point()).is_none()); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn raises_error_if_rollback_is_not_on_best_chain() { | ||
| with_db(|store| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
next.first().cloned()?