Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 7960c9e

Browse files
authored
Fix db metadata updates for existing headers (#9403)
* Fix metadata updates on existing headers * Fail set_head on ancient blocks * Fmt unrelated code
1 parent b52ddaa commit 7960c9e

File tree

2 files changed

+110
-59
lines changed

2 files changed

+110
-59
lines changed

client/db/src/lib.rs

Lines changed: 107 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,8 +1199,17 @@ impl<Block: BlockT> Backend<Block> {
11991199
let mut enacted = Vec::default();
12001200
let mut retracted = Vec::default();
12011201

1202+
let (best_number, best_hash) = best_to;
1203+
12021204
let meta = self.blockchain.meta.read();
12031205

1206+
if meta.best_number > best_number &&
1207+
(meta.best_number - best_number).saturated_into::<u64>() >
1208+
self.canonicalization_delay
1209+
{
1210+
return Err(sp_blockchain::Error::SetHeadTooOld.into())
1211+
}
1212+
12041213
// cannot find tree route with empty DB.
12051214
if meta.best_hash != Default::default() {
12061215
let tree_route = sp_blockchain::tree_route(&self.blockchain, meta.best_hash, route_to)?;
@@ -1233,13 +1242,13 @@ impl<Block: BlockT> Backend<Block> {
12331242
}
12341243
}
12351244

1236-
let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?;
1245+
let lookup_key = utils::number_and_hash_to_lookup_key(best_number, &best_hash)?;
12371246
transaction.set_from_vec(columns::META, meta_keys::BEST_BLOCK, lookup_key);
12381247
utils::insert_number_to_key_mapping(
12391248
transaction,
12401249
columns::KEY_LOOKUP,
1241-
best_to.0,
1242-
best_to.1,
1250+
best_number,
1251+
best_hash,
12431252
)?;
12441253

12451254
Ok((enacted, retracted))
@@ -1534,6 +1543,27 @@ impl<Block: BlockT> Backend<Block> {
15341543
hash, number, is_best, operation.commit_state, existing_header,
15351544
);
15361545

1546+
self.state_usage.merge_sm(operation.old_state.usage_info());
1547+
// release state reference so that it can be finalized
1548+
let cache = operation.old_state.into_cache_changes();
1549+
1550+
if finalized {
1551+
// TODO: ensure best chain contains this block.
1552+
self.ensure_sequential_finalization(header, Some(last_finalized_hash))?;
1553+
self.note_finalized(
1554+
&mut transaction,
1555+
true,
1556+
header,
1557+
hash,
1558+
&mut changes_trie_cache_ops,
1559+
&mut finalization_displaced_leaves,
1560+
operation.commit_state,
1561+
)?;
1562+
} else {
1563+
// canonicalize blocks which are old enough, regardless of finality.
1564+
self.force_delayed_canonicalize(&mut transaction, hash, *header.number())?
1565+
}
1566+
15371567
if !existing_header {
15381568
let changes_trie_config_update = operation.changes_trie_config_update;
15391569
changes_trie_cache_ops = Some(self.changes_tries_storage.commit(
@@ -1550,37 +1580,14 @@ impl<Block: BlockT> Backend<Block> {
15501580
changes_trie_cache_ops,
15511581
)?);
15521582

1553-
self.state_usage.merge_sm(operation.old_state.usage_info());
1554-
// release state reference so that it can be finalized
1555-
let cache = operation.old_state.into_cache_changes();
1556-
1557-
if finalized {
1558-
// TODO: ensure best chain contains this block.
1559-
self.ensure_sequential_finalization(header, Some(last_finalized_hash))?;
1560-
self.note_finalized(
1561-
&mut transaction,
1562-
true,
1563-
header,
1564-
hash,
1565-
&mut changes_trie_cache_ops,
1566-
&mut finalization_displaced_leaves,
1567-
operation.commit_state,
1568-
)?;
1569-
} else {
1570-
// canonicalize blocks which are old enough, regardless of finality.
1571-
self.force_delayed_canonicalize(&mut transaction, hash, *header.number())?
1572-
}
1573-
1574-
let displaced_leaf = {
1583+
{
15751584
let mut leaves = self.blockchain.leaves.write();
1576-
let displaced_leaf = leaves.import(hash, number, parent_hash);
1585+
leaves.import(hash, number, parent_hash);
15771586
leaves.prepare_transaction(
15781587
&mut transaction,
15791588
columns::META,
15801589
meta_keys::LEAF_PREFIX,
15811590
);
1582-
1583-
displaced_leaf
15841591
};
15851592

15861593
let mut children = children::read_children(
@@ -1599,28 +1606,17 @@ impl<Block: BlockT> Backend<Block> {
15991606
parent_hash,
16001607
children,
16011608
);
1609+
}
16021610

1603-
meta_updates.push(MetaUpdate {
1604-
hash,
1605-
number,
1606-
is_best: pending_block.leaf_state.is_best(),
1607-
is_finalized: finalized,
1608-
with_state: operation.commit_state,
1609-
});
1611+
meta_updates.push(MetaUpdate {
1612+
hash,
1613+
number,
1614+
is_best: pending_block.leaf_state.is_best(),
1615+
is_finalized: finalized,
1616+
with_state: operation.commit_state,
1617+
});
16101618

1611-
Some((
1612-
pending_block.header,
1613-
number,
1614-
hash,
1615-
enacted,
1616-
retracted,
1617-
displaced_leaf,
1618-
is_best,
1619-
cache,
1620-
))
1621-
} else {
1622-
None
1623-
}
1619+
Some((pending_block.header, number, hash, enacted, retracted, is_best, cache))
16241620
} else {
16251621
None
16261622
};
@@ -1660,17 +1656,7 @@ impl<Block: BlockT> Backend<Block> {
16601656
// Apply all in-memory state changes.
16611657
// Code beyond this point can't fail.
16621658

1663-
if let Some((
1664-
header,
1665-
number,
1666-
hash,
1667-
enacted,
1668-
retracted,
1669-
_displaced_leaf,
1670-
is_best,
1671-
mut cache,
1672-
)) = imported
1673-
{
1659+
if let Some((header, number, hash, enacted, retracted, is_best, mut cache)) = imported {
16741660
trace!(target: "db", "DB Commit done {:?}", hash);
16751661
let header_metadata = CachedHeaderMetadata::from(&header);
16761662
self.blockchain.insert_header_metadata(header_metadata.hash, header_metadata);
@@ -3390,4 +3376,66 @@ pub(crate) mod tests {
33903376
assert_eq!(None, backend.blockchain().header(BlockId::hash(prev_hash.clone())).unwrap());
33913377
assert!(!backend.have_state_at(&prev_hash, 1));
33923378
}
3379+
3380+
#[test]
3381+
fn test_import_existing_block_as_new_head() {
3382+
let backend: Backend<Block> = Backend::new_test(10, 3);
3383+
let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
3384+
let block1 = insert_header(&backend, 1, block0, None, Default::default());
3385+
let block2 = insert_header(&backend, 2, block1, None, Default::default());
3386+
let block3 = insert_header(&backend, 3, block2, None, Default::default());
3387+
let block4 = insert_header(&backend, 4, block3, None, Default::default());
3388+
let block5 = insert_header(&backend, 5, block4, None, Default::default());
3389+
assert_eq!(backend.blockchain().info().best_hash, block5);
3390+
3391+
// Insert 1 as best again. This should fail because canonicalization_delay == 3 and best == 5
3392+
let header = Header {
3393+
number: 1,
3394+
parent_hash: block0,
3395+
state_root: BlakeTwo256::trie_root(Vec::new()),
3396+
digest: Default::default(),
3397+
extrinsics_root: Default::default(),
3398+
};
3399+
let mut op = backend.begin_operation().unwrap();
3400+
op.set_block_data(header, None, None, None, NewBlockState::Best).unwrap();
3401+
assert!(matches!(backend.commit_operation(op), Err(sp_blockchain::Error::SetHeadTooOld)));
3402+
3403+
// Insert 2 as best again.
3404+
let header = Header {
3405+
number: 2,
3406+
parent_hash: block1,
3407+
state_root: BlakeTwo256::trie_root(Vec::new()),
3408+
digest: Default::default(),
3409+
extrinsics_root: Default::default(),
3410+
};
3411+
let mut op = backend.begin_operation().unwrap();
3412+
op.set_block_data(header, None, None, None, NewBlockState::Best).unwrap();
3413+
backend.commit_operation(op).unwrap();
3414+
assert_eq!(backend.blockchain().info().best_hash, block2);
3415+
}
3416+
3417+
#[test]
3418+
fn test_import_existing_block_as_final() {
3419+
let backend: Backend<Block> = Backend::new_test(10, 10);
3420+
let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
3421+
let block1 = insert_header(&backend, 1, block0, None, Default::default());
3422+
let _block2 = insert_header(&backend, 2, block1, None, Default::default());
3423+
// Genesis is auto finalized, the rest are not.
3424+
assert_eq!(backend.blockchain().info().finalized_hash, block0);
3425+
3426+
// Insert 1 as final again.
3427+
let header = Header {
3428+
number: 1,
3429+
parent_hash: block0,
3430+
state_root: BlakeTwo256::trie_root(Vec::new()),
3431+
digest: Default::default(),
3432+
extrinsics_root: Default::default(),
3433+
};
3434+
3435+
let mut op = backend.begin_operation().unwrap();
3436+
op.set_block_data(header, None, None, None, NewBlockState::Final).unwrap();
3437+
backend.commit_operation(op).unwrap();
3438+
3439+
assert_eq!(backend.blockchain().info().finalized_hash, block1);
3440+
}
33933441
}

primitives/blockchain/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ pub enum Error {
156156
#[error("State Database error: {0}")]
157157
StateDatabase(String),
158158

159+
#[error("Failed to set the chain head to a block that's too old.")]
160+
SetHeadTooOld,
161+
159162
#[error(transparent)]
160163
Application(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
161164

0 commit comments

Comments
 (0)