Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/src/chain/async_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,15 @@ where
self.input_best_block_index
}

/// Returns the user data of the current "input" finalized block.
///
/// Returns `None` if the input finalized block is the output finalized block (i.e. the
/// async_tree's output has caught up to the input).
Comment on lines +378 to +380
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
///
/// Returns `None` if the input finalized block is the output finalized block (i.e. the
/// async_tree's output has caught up to the input).

pub fn input_finalized_user_data(&self) -> Option<&TBl> {
self.input_finalized_index
.map(|idx| &self.non_finalized_blocks.get(idx).unwrap().user_data)
}

/// Returns the list of all non-finalized blocks that have been inserted, both input and
/// output.
///
Expand Down
192 changes: 138 additions & 54 deletions light-base/src/runtime_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1355,10 +1355,10 @@ async fn run_background<TPlat: PlatformRef>(

// TODO: DRY below
if let Some(finalized_block_runtime) = subscription.finalized_block_runtime {
let finalized_block_hash = header::hash_from_scale_encoded_header(
let warp_synced_hash = header::hash_from_scale_encoded_header(
&subscription.finalized_block_scale_encoded_header,
);
let finalized_block_height = header::decode(
let warp_synced_height = header::decode(
&subscription.finalized_block_scale_encoded_header,
background.sync_service.block_number_bytes(),
)
Expand Down Expand Up @@ -1412,64 +1412,148 @@ async fn run_background<TPlat: PlatformRef>(
}
}

background.tree = Tree::FinalizedBlockRuntimeKnown {
all_blocks_subscriptions: hashbrown::HashMap::with_capacity_and_hasher(
32,
Default::default(),
), // TODO: capacity?
pinned_blocks: BTreeMap::new(),
finalized_block: Block {
hash: finalized_block_hash,
height: finalized_block_height,
scale_encoded_header: subscription
.finalized_block_scale_encoded_header,
// Used as the tree's initial finalized state so the warp-synced block
// can be inserted as a non-finalized child and then finalized, emitting
// `Block` + `Finalized` to subscribers. Not the real chain parent of
// the warp-synced block (warp sync skips ancestry); only a tree-level
// predecessor. Pruned the moment warp-synced is finalized.
// `None` when the previous tree has no usable input-finalized block,
// in which case we fall back to the legacy single-block init.
let pre_warp_finalized: Option<Block> = match &background.tree {
Tree::FinalizedBlockRuntimeKnown {
finalized_block, ..
} => Some(Block {
hash: finalized_block.hash,
height: finalized_block.height,
scale_encoded_header: finalized_block.scale_encoded_header.clone(),
}),
Tree::FinalizedBlockRuntimeUnknown { tree } => {
tree.input_finalized_user_data().map(|b| Block {
hash: b.hash,
height: b.height,
scale_encoded_header: b.scale_encoded_header.clone(),
})
}
};
// Disable when missing or degenerately equal to warp-synced.
let use_pre_warp_finalized = pre_warp_finalized
.as_ref()
.is_some_and(|b| b.hash != warp_synced_hash);

log!(
&background.platform,
Debug,
&background.log_target,
"warp-sync-tree-init",
use_pre_warp_finalized,
pre_warp_finalized_hash = if let Some(b) = pre_warp_finalized.as_ref() {
Cow::Owned(HashDisplay(&b.hash).to_string())
} else {
Cow::Borrowed("<none>")
},
tree: {
let mut tree =
async_tree::AsyncTree::<_, Block, _>::new(async_tree::Config {
finalized_async_user_data: runtime,
retry_after_failed: Duration::from_secs(4), // TODO: hardcoded
blocks_capacity: 32,
});
warp_synced_hash = HashDisplay(&warp_synced_hash),
);

for block in subscription.non_finalized_blocks_ancestry_order {
let parent_index = if block.parent_hash == finalized_block_hash
{
None
} else {
Some(
tree.input_output_iter_unordered()
.find(|b| b.user_data.hash == block.parent_hash)
.unwrap()
.id,
)
};
// Placeholder runtime: never used (block pruned immediately); calls fail loudly.
let pre_warp_finalized_runtime = if use_pre_warp_finalized {
Arc::new(Runtime {
runtime: Err(RuntimeError::CodeNotFound),
runtime_code: None,
heap_pages: None,
code_merkle_value: None,
closest_ancestor_excluding: None,
})
} else {
runtime.clone()
};
Comment on lines +1458 to +1468
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be moved into the if below. Then we don't need to clone runtime for nothing.


let same_runtime_as_parent = same_runtime_as_parent(
let warp_synced_block = Block {
hash: warp_synced_hash,
height: warp_synced_height,
scale_encoded_header: subscription.finalized_block_scale_encoded_header,
};

let (finalized_async_user_data, finalized_block) = if use_pre_warp_finalized
{
// use_pre_warp_finalized implies pre_warp_finalized.is_some().
(pre_warp_finalized_runtime, pre_warp_finalized.unwrap())
Comment on lines +1476 to +1479
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let (finalized_async_user_data, finalized_block) = if use_pre_warp_finalized
{
// use_pre_warp_finalized implies pre_warp_finalized.is_some().
(pre_warp_finalized_runtime, pre_warp_finalized.unwrap())
let (finalized_async_user_data, finalized_block) = if let Some(finalized_block) = pre_warp_finalized.filter(|_| use_pre_warp_finalized)
{
(pre_warp_finalized_runtime, finalized_block)

} else {
(runtime.clone(), warp_synced_block.clone())
};

let mut new_tree =
async_tree::AsyncTree::<_, Block, _>::new(async_tree::Config {
finalized_async_user_data,
retry_after_failed: Duration::from_secs(4), // TODO: hardcoded
blocks_capacity: 32,
});

if use_pre_warp_finalized {
// Pre-complete the runtime so the tree-advance loop emits
// `Block` + `Finalized` without an actual download.
let warp_synced_idx = new_tree.input_insert_block(
warp_synced_block.clone(),
None,
false,
true,
);
let async_op = match new_tree
.next_necessary_async_op(&background.platform.now())
{
async_tree::NextNecessaryAsyncOp::Ready(op) => op,
async_tree::NextNecessaryAsyncOp::NotReady { .. } => unreachable!(
"warp-synced block just inserted with pending async op"
),
};
debug_assert_eq!(async_op.block_index, warp_synced_idx);
new_tree.async_op_finished(async_op.id, runtime);
new_tree.input_finalize(warp_synced_idx);
}
Comment on lines +1491 to +1511
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what this changes?


for block in subscription.non_finalized_blocks_ancestry_order {
// Parent is the tree's outer finalized (None) or already in the tree.
let parent_index = if block.parent_hash == finalized_block.hash {
None
} else {
Some(
new_tree
.input_output_iter_unordered()
.find(|b| b.user_data.hash == block.parent_hash)
.unwrap()
.id,
)
};
let same_runtime_as_parent = same_runtime_as_parent(
&block.scale_encoded_header,
background.sync_service.block_number_bytes(),
);
let _ = new_tree.input_insert_block(
Block {
hash: header::hash_from_scale_encoded_header(
&block.scale_encoded_header,
),
height: header::decode(
&block.scale_encoded_header,
background.sync_service.block_number_bytes(),
);
let _ = tree.input_insert_block(
Block {
hash: header::hash_from_scale_encoded_header(
&block.scale_encoded_header,
),
height: header::decode(
&block.scale_encoded_header,
background.sync_service.block_number_bytes(),
)
.unwrap()
.number, // TODO: consider feeding the information from the sync service?
scale_encoded_header: block.scale_encoded_header,
},
parent_index,
same_runtime_as_parent,
block.is_new_best,
);
}
)
.unwrap()
.number,
scale_encoded_header: block.scale_encoded_header,
},
parent_index,
same_runtime_as_parent,
block.is_new_best,
);
}

tree
},
background.tree = Tree::FinalizedBlockRuntimeKnown {
all_blocks_subscriptions: hashbrown::HashMap::with_capacity_and_hasher(
32,
Default::default(),
), // TODO: capacity?
pinned_blocks: BTreeMap::new(),
finalized_block,
tree: new_tree,
};
} else {
background.tree = Tree::FinalizedBlockRuntimeUnknown {
Expand Down
Loading