Skip to content

Commit 2ba89a2

Browse files
authored
Merge pull request #330 from rustaceanrob/cf-headers-4-12
Replace `CFHeaderChain` and `FilterChain`
2 parents c0400db + c3bb6c4 commit 2ba89a2

File tree

11 files changed

+432
-467
lines changed

11 files changed

+432
-467
lines changed

src/chain/chain.rs

Lines changed: 215 additions & 163 deletions
Large diffs are not rendered by default.

src/chain/graph.rs

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use bitcoin::{
77
Network, Work,
88
};
99

10-
use super::IndexedHeader;
10+
use super::{FilterCommitment, IndexedHeader};
1111

1212
const LOCATOR_INDEX: &[u32] = &[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024];
1313

@@ -110,7 +110,8 @@ pub(crate) struct BlockNode {
110110
pub height: Height,
111111
pub header: Header,
112112
pub acc_work: Work,
113-
pub filter_hash: Option<FilterHash>,
113+
pub filter_commitment: Option<FilterCommitment>,
114+
pub filter_checked: bool,
114115
}
115116

116117
impl BlockNode {
@@ -119,7 +120,8 @@ impl BlockNode {
119120
height,
120121
header,
121122
acc_work,
122-
filter_hash: None,
123+
filter_commitment: None,
124+
filter_checked: false,
123125
}
124126
}
125127
}
@@ -404,6 +406,10 @@ impl BlockTree {
404406
self.headers.get(&hash).map(|node| node.height.to_u32())
405407
}
406408

409+
pub(crate) fn header_at_hash(&self, hash: BlockHash) -> Option<Header> {
410+
self.headers.get(&hash).map(|node| node.header)
411+
}
412+
407413
pub(crate) fn height(&self) -> u32 {
408414
self.active_tip.height.to_u32()
409415
}
@@ -417,13 +423,82 @@ impl BlockTree {
417423
}
418424

419425
pub(crate) fn filter_hash(&self, block_hash: BlockHash) -> Option<FilterHash> {
420-
self.headers.get(&block_hash)?.filter_hash
426+
Some(
427+
self.headers
428+
.get(&block_hash)?
429+
.filter_commitment?
430+
.filter_hash,
431+
)
432+
}
433+
434+
pub(crate) fn filter_commitment(&self, block_hash: BlockHash) -> Option<&FilterCommitment> {
435+
self.headers.get(&block_hash)?.filter_commitment.as_ref()
421436
}
422437

423438
pub(crate) fn filter_hash_at_height(&self, height: impl Into<Height>) -> Option<FilterHash> {
424439
let height = height.into();
425440
let hash = self.canonical_hashes.get(&height)?;
426-
self.headers.get(hash)?.filter_hash
441+
Some(self.headers.get(hash)?.filter_commitment?.filter_hash)
442+
}
443+
444+
pub(crate) fn set_commitment(&mut self, commitment: FilterCommitment, hash: BlockHash) {
445+
if let Some(node) = self.headers.get_mut(&hash) {
446+
node.filter_commitment = Some(commitment)
447+
}
448+
}
449+
450+
pub(crate) fn check_filter(&mut self, hash: BlockHash) {
451+
if let Some(node) = self.headers.get_mut(&hash) {
452+
node.filter_checked = true
453+
}
454+
}
455+
456+
pub(crate) fn reset_all_filters(&mut self) {
457+
let mut curr = self.tip_hash();
458+
while let Some(node) = self.headers.get_mut(&curr) {
459+
match self.headers.get_mut(&curr) {
460+
Some(node) => {
461+
node.filter_checked = false;
462+
curr = node.header.prev_blockhash;
463+
}
464+
None => break,
465+
}
466+
}
467+
for fork in &self.candidate_forks {
468+
curr = fork.hash;
469+
while let Some(node) = self.headers.get_mut(&curr) {
470+
match self.headers.get_mut(&curr) {
471+
Some(node) => {
472+
if !node.filter_checked {
473+
break;
474+
}
475+
node.filter_checked = false;
476+
curr = node.header.prev_blockhash;
477+
}
478+
None => break,
479+
}
480+
}
481+
}
482+
}
483+
484+
pub(crate) fn filter_headers_synced(&self) -> bool {
485+
self.iter_data()
486+
.map(|node| node.filter_commitment)
487+
.all(|commitment| commitment.is_some())
488+
}
489+
490+
pub(crate) fn filters_synced(&self) -> bool {
491+
self.iter_data().all(|node| node.filter_checked)
492+
}
493+
494+
pub(crate) fn total_filters_synced(&self) -> u32 {
495+
self.iter_data().filter(|node| node.filter_checked).count() as u32
496+
}
497+
498+
pub(crate) fn total_filter_headers_synced(&self) -> u32 {
499+
self.iter_data()
500+
.filter(|node| node.filter_commitment.is_some())
501+
.count() as u32
427502
}
428503

429504
pub(crate) fn locators(&self) -> Vec<BlockHash> {
@@ -446,20 +521,27 @@ impl BlockTree {
446521
self.headers.len()
447522
}
448523

449-
pub(crate) fn iter(&self) -> BlockTreeIterator {
450-
BlockTreeIterator {
524+
pub(crate) fn iter_data(&self) -> BlockNodeIterator {
525+
BlockNodeIterator {
526+
block_tree: self,
527+
current: self.active_tip.hash,
528+
}
529+
}
530+
531+
pub(crate) fn iter_headers(&self) -> BlockHeaderIterator {
532+
BlockHeaderIterator {
451533
block_tree: self,
452534
current: self.active_tip.hash,
453535
}
454536
}
455537
}
456538

457-
pub(crate) struct BlockTreeIterator<'a> {
539+
pub(crate) struct BlockHeaderIterator<'a> {
458540
block_tree: &'a BlockTree,
459541
current: BlockHash,
460542
}
461543

462-
impl Iterator for BlockTreeIterator<'_> {
544+
impl Iterator for BlockHeaderIterator<'_> {
463545
type Item = IndexedHeader;
464546

465547
fn next(&mut self) -> Option<Self::Item> {
@@ -469,6 +551,21 @@ impl Iterator for BlockTreeIterator<'_> {
469551
}
470552
}
471553

554+
pub(crate) struct BlockNodeIterator<'a> {
555+
block_tree: &'a BlockTree,
556+
current: BlockHash,
557+
}
558+
559+
impl<'a> Iterator for BlockNodeIterator<'a> {
560+
type Item = &'a BlockNode;
561+
562+
fn next(&mut self) -> Option<Self::Item> {
563+
let node = self.block_tree.headers.get(&self.current)?;
564+
self.current = node.header.prev_blockhash;
565+
Some(node)
566+
}
567+
}
568+
472569
#[cfg(test)]
473570
mod tests {
474571
use bitcoin::consensus::deserialize;

src/chain/mod.rs

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ pub(crate) mod header_batch;
1414

1515
use std::collections::HashMap;
1616

17-
use bitcoin::block::Header;
17+
use bitcoin::{block::Header, BlockHash, FilterHash, FilterHeader};
1818

19-
use crate::network::PeerId;
19+
use crate::{filters::cfheader_batch::CFHeaderBatch, network::PeerId};
2020

2121
type Height = u32;
2222

@@ -35,6 +35,81 @@ impl IndexedHeader {
3535
}
3636
}
3737

38+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
39+
pub(crate) struct FilterCommitment {
40+
pub header: FilterHeader,
41+
pub filter_hash: FilterHash,
42+
}
43+
44+
#[derive(Debug, Clone)]
45+
pub(crate) struct FilterRequestState {
46+
pub last_filter_request: Option<FilterRequest>,
47+
pub last_filter_header_request: Option<FilterHeaderRequest>,
48+
pub pending_batch: Option<(PeerId, CFHeaderBatch)>,
49+
pub agreement_state: FilterHeaderAgreements,
50+
}
51+
52+
impl FilterRequestState {
53+
pub(crate) fn new(required: u8) -> Self {
54+
Self {
55+
last_filter_request: None,
56+
last_filter_header_request: None,
57+
pending_batch: None,
58+
agreement_state: FilterHeaderAgreements::new(required),
59+
}
60+
}
61+
}
62+
63+
#[derive(Debug, Clone, Copy)]
64+
pub(crate) struct FilterRequest {
65+
#[allow(unused)]
66+
pub start_height: u32,
67+
pub stop_hash: BlockHash,
68+
}
69+
70+
#[derive(Debug, Clone, Copy)]
71+
pub(crate) struct FilterHeaderRequest {
72+
pub start_height: u32,
73+
pub stop_hash: BlockHash,
74+
pub expected_prev_filter_header: Option<FilterHeader>,
75+
}
76+
77+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78+
pub(crate) struct FilterHeaderAgreements {
79+
current: u8,
80+
required: u8,
81+
}
82+
83+
impl FilterHeaderAgreements {
84+
pub(crate) fn new(required: u8) -> Self {
85+
Self {
86+
current: 0,
87+
required,
88+
}
89+
}
90+
91+
pub(crate) fn got_agreement(&mut self) {
92+
self.current += 1;
93+
}
94+
95+
pub(crate) fn enough_agree(&self) -> bool {
96+
self.current.ge(&self.required)
97+
}
98+
99+
pub(crate) fn reset_agreements(&mut self) {
100+
self.current = 0;
101+
}
102+
}
103+
104+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105+
pub(crate) enum CFHeaderChanges {
106+
AddedToQueue,
107+
Extended,
108+
// Unfortunately, auditing each peer by reconstruction the filter would be costly in network
109+
// and compute. Instead it is easier to disconnect from all peers and try again.
110+
Conflict,
111+
}
112+
38113
#[derive(Debug)]
39114
pub(crate) struct HeightMonitor {
40115
map: HashMap<PeerId, Height>,

src/dialog.rs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use tokio::sync::mpsc::{Sender, UnboundedSender};
22

3-
use super::messages::{Event, Info, Progress, Warning};
3+
use super::messages::{Event, Info, Warning};
44
use crate::LogLevel;
55

66
#[derive(Debug, Clone)]
@@ -33,32 +33,6 @@ impl Dialog {
3333
let _ = self.log_tx.send(dialog.into()).await;
3434
}
3535

36-
pub(crate) async fn chain_update(
37-
&self,
38-
num_headers: u32,
39-
num_cf_headers: u32,
40-
num_filters: u32,
41-
best_height: u32,
42-
) {
43-
if matches!(self.log_level, LogLevel::Debug | LogLevel::Info) {
44-
let _ = self
45-
.info_tx
46-
.send(Info::Progress(Progress::new(
47-
num_cf_headers,
48-
num_filters,
49-
best_height,
50-
)))
51-
.await;
52-
}
53-
if matches!(self.log_level, LogLevel::Debug) {
54-
let message = format!(
55-
"Headers ({}/{}) Compact Filter Headers ({}/{}) Filters ({}/{})",
56-
num_headers, best_height, num_cf_headers, best_height, num_filters, best_height
57-
);
58-
let _ = self.log_tx.send(message).await;
59-
}
60-
}
61-
6236
pub(crate) fn send_warning(&self, warning: Warning) {
6337
let _ = self.warn_tx.send(warning);
6438
}

src/filters/cfheader_batch.rs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
use bitcoin::{p2p::message_filter::CFHeaders, BlockHash, FilterHash, FilterHeader};
1+
use bitcoin::{p2p::message_filter::CFHeaders, BlockHash, FilterHeader};
22

3+
use crate::chain::FilterCommitment;
4+
5+
#[derive(Debug, Clone, PartialEq, Eq)]
36
pub(crate) struct CFHeaderBatch {
4-
inner: Vec<(FilterHeader, FilterHash)>,
7+
inner: Vec<FilterCommitment>,
58
prev_filter_header: FilterHeader,
69
stop_hash: BlockHash,
710
}
@@ -11,11 +14,14 @@ impl CFHeaderBatch {
1114
// the CFHeader message may make it easier to detect
1215
// faulty peers sooner
1316
pub(crate) fn new(batch: CFHeaders) -> Self {
14-
let mut headers: Vec<(FilterHeader, FilterHash)> = vec![];
17+
let mut headers: Vec<FilterCommitment> = vec![];
1518
let mut prev_header = batch.previous_filter_header;
1619
for hash in batch.filter_hashes {
1720
let next_header = hash.filter_header(&prev_header);
18-
headers.push((next_header, hash));
21+
headers.push(FilterCommitment {
22+
header: next_header,
23+
filter_hash: hash,
24+
});
1925
prev_header = next_header;
2026
}
2127
Self {
@@ -29,26 +35,17 @@ impl CFHeaderBatch {
2935
&self.prev_filter_header
3036
}
3137

32-
pub(crate) fn stop_hash(&self) -> &BlockHash {
33-
&self.stop_hash
34-
}
35-
36-
pub(crate) fn len(&self) -> usize {
37-
self.inner.len()
38+
pub(crate) fn stop_hash(&self) -> BlockHash {
39+
self.stop_hash
3840
}
3941

40-
#[allow(dead_code)]
41-
pub(crate) fn inner(&self) -> Vec<(FilterHeader, FilterHash)> {
42-
self.inner.clone()
42+
pub(crate) fn len(&self) -> u32 {
43+
self.inner.len() as u32
4344
}
4445

45-
pub(crate) fn take_inner(&mut self) -> Vec<(FilterHeader, FilterHash)> {
46+
pub(crate) fn take_inner(&mut self) -> Vec<FilterCommitment> {
4647
core::mem::take(&mut self.inner)
4748
}
48-
49-
pub(crate) fn last_header(&self) -> Option<FilterHeader> {
50-
self.inner.last().map(|(header, _)| *header)
51-
}
5249
}
5350

5451
impl From<CFHeaders> for CFHeaderBatch {

0 commit comments

Comments
 (0)