Skip to content

Commit 0239f05

Browse files
committed
f - Simplify fork detection logic
1 parent 26b66fd commit 0239f05

File tree

1 file changed

+43
-68
lines changed
  • lightning-block-sync/src

1 file changed

+43
-68
lines changed

lightning-block-sync/src/lib.rs

Lines changed: 43 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,17 @@ struct ChainNotifier<C: Cache> {
194194
header_cache: C,
195195
}
196196

197-
/// Steps outlining changes needed to be made to the chain in order to transform it from having one
198-
/// chain tip to another.
199-
enum ForkStep {
200-
ForkPoint(ValidatedBlockHeader),
201-
DisconnectBlock(ValidatedBlockHeader),
202-
ConnectBlock(ValidatedBlockHeader),
197+
/// Changes made to the chain between subsequent polls that transformed it from having one chain tip
198+
/// to another.
199+
///
200+
/// Blocks are given in height-descending order. Therefore, blocks are first disconnected in order
201+
/// before new blocks are connected in reverse order.
202+
struct ChainDifference {
203+
/// Blocks that were disconnected from the chain since the last poll.
204+
disconnected_blocks: Vec<ValidatedBlockHeader>,
205+
206+
/// Blocks that were connected to the chain since the last poll.
207+
connected_blocks: Vec<ValidatedBlockHeader>,
203208
}
204209

205210
impl<C: Cache> ChainNotifier<C> {
@@ -217,81 +222,51 @@ impl<C: Cache> ChainNotifier<C> {
217222
chain_poller: &mut P,
218223
chain_listener: &mut L,
219224
) -> Result<(), (BlockSourceError, Option<ValidatedBlockHeader>)> {
220-
let mut events = self.find_fork(new_header, old_header, chain_poller).await.map_err(|e| (e, None))?;
221-
222-
let mut last_disconnect_tip = None;
223-
let mut new_tip = None;
224-
for event in events.iter() {
225-
match &event {
226-
&ForkStep::DisconnectBlock(ref header) => {
227-
println!("Disconnecting block {}", header.block_hash);
228-
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {
229-
assert_eq!(cached_header, *header);
230-
}
231-
chain_listener.block_disconnected(&header.header, header.height);
232-
last_disconnect_tip = Some(header.header.prev_blockhash);
233-
},
234-
&ForkStep::ForkPoint(ref header) => {
235-
new_tip = Some(*header);
236-
},
237-
_ => {},
225+
let mut difference = self.find_difference(new_header, old_header, chain_poller).await
226+
.map_err(|e| (e, None))?;
227+
228+
let mut new_tip = *old_header;
229+
for header in difference.disconnected_blocks.drain(..) {
230+
println!("Disconnecting block {}", header.block_hash);
231+
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {
232+
assert_eq!(cached_header, header);
238233
}
234+
chain_listener.block_disconnected(&header.header, header.height);
235+
new_tip = header;
239236
}
240237

241-
// If blocks were disconnected, new blocks will connect starting from the fork point.
242-
// Otherwise, there was no fork, so new blocks connect starting from the old tip.
243-
assert_eq!(last_disconnect_tip.is_some(), new_tip.is_some());
244-
if let &Some(ref tip_header) = &new_tip {
245-
debug_assert_eq!(tip_header.header.block_hash(), *last_disconnect_tip.as_ref().unwrap());
246-
} else {
247-
new_tip = Some(*old_header);
248-
}
238+
for header in difference.connected_blocks.drain(..).rev() {
239+
let block = chain_poller
240+
.fetch_block(&header).await
241+
.or_else(|e| Err((e, Some(new_tip))))?;
242+
debug_assert_eq!(block.block_hash, header.block_hash);
249243

250-
for event in events.drain(..).rev() {
251-
if let ForkStep::ConnectBlock(header) = event {
252-
let block = chain_poller
253-
.fetch_block(&header).await
254-
.or_else(|e| Err((e, new_tip)))?;
255-
debug_assert_eq!(block.block_hash, header.block_hash);
256-
257-
println!("Connecting block {}", header.block_hash);
258-
self.header_cache.block_connected(header.block_hash, header);
259-
chain_listener.block_connected(&block, header.height);
260-
new_tip = Some(header);
261-
}
244+
println!("Connecting block {}", header.block_hash);
245+
self.header_cache.block_connected(header.block_hash, header);
246+
chain_listener.block_connected(&block, header.height);
247+
new_tip = header;
262248
}
249+
263250
Ok(())
264251
}
265252

253+
/// Returns the changes needed to produce the chain with `current_header` as its tip from the
254+
/// chain with `prev_header` as its tip.
255+
///
266256
/// Walks backwards from `current_header` and `prev_header`, finding the common ancestor.
267-
/// Returns the steps needed to produce the chain with `current_header` as its tip from the
268-
/// chain with `prev_header` as its tip. There is no ordering guarantee between different
269-
/// `ForkStep` types, but `DisconnectBlock` and `ConnectBlock` are each returned in
270-
/// height-descending order.
271-
async fn find_fork<P: Poll>(
257+
async fn find_difference<P: Poll>(
272258
&self,
273259
current_header: ValidatedBlockHeader,
274260
prev_header: &ValidatedBlockHeader,
275261
chain_poller: &mut P,
276-
) -> BlockSourceResult<Vec<ForkStep>> {
277-
let mut steps = Vec::new();
262+
) -> BlockSourceResult<ChainDifference> {
263+
let mut disconnected_blocks = Vec::new();
264+
let mut connected_blocks = Vec::new();
278265
let mut current = current_header;
279266
let mut previous = *prev_header;
280267
loop {
281-
// Found the parent block.
282-
if current.header.prev_blockhash == previous.block_hash {
283-
debug_assert_eq!(current.height, previous.height + 1);
284-
steps.push(ForkStep::ConnectBlock(current));
285-
break;
286-
}
287-
288-
// Found a chain fork.
289-
if current.header.prev_blockhash == previous.header.prev_blockhash {
290-
debug_assert_eq!(current.height, previous.height);
291-
let fork_point = self.look_up_previous_header(chain_poller, &previous).await?;
292-
steps.push(ForkStep::DisconnectBlock(previous));
293-
steps.push(ForkStep::ConnectBlock(current));
294-
steps.push(ForkStep::ForkPoint(fork_point));
268+
// Found the common ancestor.
269+
if current.block_hash == previous.block_hash {
295270
break;
296271
}
297272

@@ -300,16 +275,16 @@ impl<C: Cache> ChainNotifier<C> {
300275
let current_height = current.height;
301276
let previous_height = previous.height;
302277
if current_height <= previous_height {
303-
steps.push(ForkStep::DisconnectBlock(previous));
278+
disconnected_blocks.push(previous);
304279
previous = self.look_up_previous_header(chain_poller, &previous).await?;
305280
}
306281
if current_height >= previous_height {
307-
steps.push(ForkStep::ConnectBlock(current));
282+
connected_blocks.push(current);
308283
current = self.look_up_previous_header(chain_poller, &current).await?;
309284
}
310285
}
311286

312-
Ok(steps)
287+
Ok(ChainDifference { disconnected_blocks, connected_blocks })
313288
}
314289

315290
/// Returns the previous header for the given header, either by looking it up in the cache or

0 commit comments

Comments
 (0)