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

Commit d8df977

Browse files
arkparrphmeier
authored andcommitted
Allow import withouth state verification (#4031)
* Allow import without state verification * Explicit None Co-Authored-By: Robert Habermeier <[email protected]>
1 parent 5026f21 commit d8df977

File tree

20 files changed

+232
-88
lines changed

20 files changed

+232
-88
lines changed

core/client/db/src/lib.rs

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ pub struct BlockImportOperation<Block: BlockT, H: Hasher> {
457457
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
458458
finalized_blocks: Vec<(BlockId<Block>, Option<Justification>)>,
459459
set_head: Option<BlockId<Block>>,
460+
commit_state: bool,
460461
}
461462

462463
impl<Block: BlockT, H: Hasher> BlockImportOperation<Block, H> {
@@ -531,6 +532,7 @@ impl<Block> client::backend::BlockImportOperation<Block, Blake2Hasher>
531532
);
532533

533534
self.db_updates = transaction;
535+
self.commit_state = true;
534536
Ok(root)
535537
}
536538

@@ -783,6 +785,7 @@ pub struct Backend<Block: BlockT> {
783785
canonicalization_delay: u64,
784786
shared_cache: SharedCache<Block, Blake2Hasher>,
785787
import_lock: Mutex<()>,
788+
is_archive: bool,
786789
}
787790

788791
impl<Block: BlockT<Hash=H256>> Backend<Block> {
@@ -843,6 +846,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
843846
config.state_cache_child_ratio.unwrap_or(DEFAULT_CHILD_RATIO),
844847
),
845848
import_lock: Default::default(),
849+
is_archive: is_archive_pruning,
846850
})
847851
}
848852

@@ -894,6 +898,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
894898
inmem
895899
}
896900

901+
/// Returns total numbet of blocks (headers) in the block DB.
902+
#[cfg(feature = "test-helpers")]
903+
pub fn blocks_count(&self) -> u64 {
904+
self.blockchain.db.iter(columns::HEADER).count() as u64
905+
}
906+
897907
/// Read (from storage or cache) changes trie config.
898908
///
899909
/// Currently changes tries configuration is set up once (at genesis) and could not
@@ -1115,7 +1125,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
11151125
);
11161126

11171127
transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode());
1118-
if let Some(body) = pending_block.body {
1128+
if let Some(body) = &pending_block.body {
11191129
transaction.put(columns::BODY, &lookup_key, &body.encode());
11201130
}
11211131
if let Some(justification) = pending_block.justification {
@@ -1127,21 +1137,26 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> {
11271137
transaction.put(columns::META, meta_keys::GENESIS_HASH, hash.as_ref());
11281138
}
11291139

1130-
let mut changeset: state_db::ChangeSet<Vec<u8>> = state_db::ChangeSet::default();
1131-
for (key, (val, rc)) in operation.db_updates.drain() {
1132-
if rc > 0 {
1133-
changeset.inserted.push((key, val.to_vec()));
1134-
} else if rc < 0 {
1135-
changeset.deleted.push(key);
1140+
let finalized = if operation.commit_state {
1141+
let mut changeset: state_db::ChangeSet<Vec<u8>> = state_db::ChangeSet::default();
1142+
for (key, (val, rc)) in operation.db_updates.drain() {
1143+
if rc > 0 {
1144+
changeset.inserted.push((key, val.to_vec()));
1145+
} else if rc < 0 {
1146+
changeset.deleted.push(key);
1147+
}
11361148
}
1137-
}
1138-
let number_u64 = number.saturated_into::<u64>();
1139-
let commit = self.storage.state_db.insert_block(&hash, number_u64, &pending_block.header.parent_hash(), changeset)
1140-
.map_err(|e: state_db::Error<io::Error>| client::error::Error::from(format!("State database error: {:?}", e)))?;
1141-
apply_state_commit(&mut transaction, commit);
1142-
1143-
// Check if need to finalize. Genesis is always finalized instantly.
1144-
let finalized = number_u64 == 0 || pending_block.leaf_state.is_final();
1149+
let number_u64 = number.saturated_into::<u64>();
1150+
let commit = self.storage.state_db.insert_block(&hash, number_u64, &pending_block.header.parent_hash(), changeset)
1151+
.map_err(|e: state_db::Error<io::Error>| client::error::Error::from(format!("State database error: {:?}", e)))?;
1152+
apply_state_commit(&mut transaction, commit);
1153+
1154+
// Check if need to finalize. Genesis is always finalized instantly.
1155+
let finalized = number_u64 == 0 || pending_block.leaf_state.is_final();
1156+
finalized
1157+
} else {
1158+
false
1159+
};
11451160

11461161
let header = &pending_block.header;
11471162
let is_best = pending_block.leaf_state.is_best();
@@ -1347,6 +1362,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
13471362
aux_ops: Vec::new(),
13481363
finalized_blocks: Vec::new(),
13491364
set_head: None,
1365+
commit_state: false,
13501366
})
13511367
}
13521368

@@ -1356,6 +1372,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
13561372
block: BlockId<Block>,
13571373
) -> ClientResult<()> {
13581374
operation.old_state = self.state_at(block)?;
1375+
operation.commit_state = true;
13591376
Ok(())
13601377
}
13611378

@@ -1478,6 +1495,9 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
14781495
match self.blockchain.header(block) {
14791496
Ok(Some(ref hdr)) => {
14801497
let hash = hdr.hash();
1498+
if !self.have_state_at(&hash, *hdr.number()) {
1499+
return Err(client::error::Error::UnknownBlock(format!("State already discarded for {:?}", block)))
1500+
}
14811501
if let Ok(()) = self.storage.state_db.pin(&hash) {
14821502
let root = H256::from_slice(hdr.state_root().as_ref());
14831503
let db_state = DbState::new(self.storage.clone(), root);
@@ -1493,7 +1513,16 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
14931513
}
14941514

14951515
fn have_state_at(&self, hash: &Block::Hash, number: NumberFor<Block>) -> bool {
1496-
!self.storage.state_db.is_pruned(hash, number.saturated_into::<u64>())
1516+
if self.is_archive {
1517+
match self.blockchain.header(BlockId::Hash(hash.clone())) {
1518+
Ok(Some(header)) => {
1519+
state_machine::Storage::get(self.storage.as_ref(), &header.state_root(), (&[], None)).unwrap_or(None).is_some()
1520+
},
1521+
_ => false,
1522+
}
1523+
} else {
1524+
!self.storage.state_db.is_pruned(hash, number.saturated_into::<u64>())
1525+
}
14971526
}
14981527

14991528
fn destroy_state(&self, state: Self::State) -> ClientResult<()> {
@@ -1581,7 +1610,7 @@ mod tests {
15811610
};
15821611
let mut op = backend.begin_operation().unwrap();
15831612
backend.begin_state_operation(&mut op, block_id).unwrap();
1584-
op.set_block_data(header, None, None, NewBlockState::Best).unwrap();
1613+
op.set_block_data(header, Some(Vec::new()), None, NewBlockState::Best).unwrap();
15851614
op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear)).unwrap();
15861615
backend.commit_operation(op).unwrap();
15871616

core/client/src/client.rs

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -838,15 +838,22 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
838838
finalized,
839839
auxiliary,
840840
fork_choice,
841+
allow_missing_state,
841842
} = import_block;
842843

843844
assert!(justification.is_some() && finalized || justification.is_none());
844845

845846
let parent_hash = header.parent_hash().clone();
847+
let mut enact_state = true;
846848

847-
match self.backend.blockchain().status(BlockId::Hash(parent_hash))? {
848-
blockchain::BlockStatus::InChain => {},
849-
blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
849+
match self.block_status(&BlockId::Hash(parent_hash))? {
850+
BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
851+
BlockStatus::InChainWithState | BlockStatus::Queued => {},
852+
BlockStatus::InChainPruned if allow_missing_state => {
853+
enact_state = false;
854+
},
855+
BlockStatus::InChainPruned => return Ok(ImportResult::MissingState),
856+
BlockStatus::KnownBad => return Ok(ImportResult::KnownBad),
850857
}
851858

852859
let import_headers = if post_digests.is_empty() {
@@ -875,6 +882,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
875882
finalized,
876883
auxiliary,
877884
fork_choice,
885+
enact_state,
878886
);
879887

880888
if let Ok(ImportResult::Imported(ref aux)) = result {
@@ -902,6 +910,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
902910
finalized: bool,
903911
aux: Vec<(Vec<u8>, Option<Vec<u8>>)>,
904912
fork_choice: ForkChoiceStrategy,
913+
enact_state: bool,
905914
) -> error::Result<ImportResult> where
906915
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone,
907916
{
@@ -927,22 +936,39 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
927936
BlockOrigin::Genesis | BlockOrigin::NetworkInitialSync | BlockOrigin::File => false,
928937
};
929938

930-
self.backend.begin_state_operation(&mut operation.op, BlockId::Hash(parent_hash))?;
939+
let storage_changes = match &body {
940+
Some(body) if enact_state => {
941+
self.backend.begin_state_operation(&mut operation.op, BlockId::Hash(parent_hash))?;
931942

932-
// ensure parent block is finalized to maintain invariant that
933-
// finality is called sequentially.
934-
if finalized {
935-
self.apply_finality_with_block_hash(operation, parent_hash, None, info.best_hash, make_notifications)?;
936-
}
943+
// ensure parent block is finalized to maintain invariant that
944+
// finality is called sequentially.
945+
if finalized {
946+
self.apply_finality_with_block_hash(operation, parent_hash, None, info.best_hash, make_notifications)?;
947+
}
937948

938-
// FIXME #1232: correct path logic for when to execute this function
939-
let (storage_update, changes_update, storage_changes) = self.block_execution(
940-
&operation.op,
941-
&import_headers,
942-
origin,
943-
hash,
944-
body.clone(),
945-
)?;
949+
// FIXME #1232: correct path logic for when to execute this function
950+
let (storage_update, changes_update, storage_changes) = self.block_execution(
951+
&operation.op,
952+
&import_headers,
953+
origin,
954+
hash,
955+
&body,
956+
)?;
957+
958+
operation.op.update_cache(new_cache);
959+
if let Some(storage_update) = storage_update {
960+
operation.op.update_db_storage(storage_update)?;
961+
}
962+
if let Some(storage_changes) = storage_changes.clone() {
963+
operation.op.update_storage(storage_changes.0, storage_changes.1)?;
964+
}
965+
if let Some(Some(changes_update)) = changes_update {
966+
operation.op.update_changes_trie(changes_update)?;
967+
}
968+
storage_changes
969+
},
970+
_ => None,
971+
};
946972

947973
let is_new_best = finalized || match fork_choice {
948974
ForkChoiceStrategy::LongestChain => import_headers.post().number() > &info.best_number,
@@ -977,17 +1003,6 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
9771003
leaf_state,
9781004
)?;
9791005

980-
operation.op.update_cache(new_cache);
981-
if let Some(storage_update) = storage_update {
982-
operation.op.update_db_storage(storage_update)?;
983-
}
984-
if let Some(storage_changes) = storage_changes.clone() {
985-
operation.op.update_storage(storage_changes.0, storage_changes.1)?;
986-
}
987-
if let Some(Some(changes_update)) = changes_update {
988-
operation.op.update_changes_trie(changes_update)?;
989-
}
990-
9911006
operation.op.insert_aux(aux)?;
9921007

9931008
if make_notifications {
@@ -1014,7 +1029,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
10141029
import_headers: &PrePostHeader<Block::Header>,
10151030
origin: BlockOrigin,
10161031
hash: Block::Hash,
1017-
body: Option<Vec<Block::Extrinsic>>,
1032+
body: &[Block::Extrinsic],
10181033
) -> error::Result<(
10191034
Option<StorageUpdate<B, Block>>,
10201035
Option<Option<ChangesUpdate<Block>>>,
@@ -1052,7 +1067,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
10521067

10531068
let encoded_block = <Block as BlockT>::encode_from(
10541069
import_headers.pre(),
1055-
&body.unwrap_or_default()
1070+
body,
10561071
);
10571072

10581073
let (_, storage_update, changes_update) = self.executor
@@ -1523,7 +1538,7 @@ impl<'a, B, E, Block, RA> consensus::BlockImport<Block> for &'a Client<B, E, Blo
15231538
&mut self,
15241539
block: BlockCheckParams<Block>,
15251540
) -> Result<ImportResult, Self::Error> {
1526-
let BlockCheckParams { hash, number, parent_hash } = block;
1541+
let BlockCheckParams { hash, number, parent_hash, allow_missing_state } = block;
15271542

15281543
if let Some(h) = self.fork_blocks.as_ref().and_then(|x| x.get(&number)) {
15291544
if &hash != h {
@@ -1541,7 +1556,9 @@ impl<'a, B, E, Block, RA> consensus::BlockImport<Block> for &'a Client<B, E, Blo
15411556
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
15421557
{
15431558
BlockStatus::InChainWithState | BlockStatus::Queued => {},
1544-
BlockStatus::Unknown | BlockStatus::InChainPruned => return Ok(ImportResult::UnknownParent),
1559+
BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
1560+
BlockStatus::InChainPruned if allow_missing_state => {},
1561+
BlockStatus::InChainPruned => return Ok(ImportResult::MissingState),
15451562
BlockStatus::KnownBad => return Ok(ImportResult::KnownBad),
15461563
}
15471564

@@ -1553,7 +1570,6 @@ impl<'a, B, E, Block, RA> consensus::BlockImport<Block> for &'a Client<B, E, Blo
15531570
BlockStatus::KnownBad => return Ok(ImportResult::KnownBad),
15541571
}
15551572

1556-
15571573
Ok(ImportResult::imported(false))
15581574
}
15591575
}

core/consensus/aura/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ impl<H, B, C, E, I, P, Error, SO> slots::SimpleSlotWorker<B> for AuraWorker<C, E
267267
finalized: false,
268268
auxiliary: Vec::new(),
269269
fork_choice: ForkChoiceStrategy::LongestChain,
270+
allow_missing_state: false,
270271
}
271272
})
272273
}
@@ -570,6 +571,7 @@ impl<B: BlockT, C, P, T> Verifier<B> for AuraVerifier<C, P, T> where
570571
justification,
571572
auxiliary: Vec::new(),
572573
fork_choice: ForkChoiceStrategy::LongestChain,
574+
allow_missing_state: false,
573575
};
574576

575577
Ok((block_import_params, maybe_keys))

core/consensus/babe/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ impl<B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<B, C, E, I
430430
// option to specify one.
431431
// https://github.com/paritytech/substrate/issues/3623
432432
fork_choice: ForkChoiceStrategy::LongestChain,
433+
allow_missing_state: false,
433434
}
434435
})
435436
}
@@ -741,6 +742,7 @@ impl<B, E, Block, RA, PRA> Verifier<Block> for BabeVerifier<B, E, Block, RA, PRA
741742
// option to specify one.
742743
// https://github.com/paritytech/substrate/issues/3623
743744
fork_choice: ForkChoiceStrategy::LongestChain,
745+
allow_missing_state: false,
744746
};
745747

746748
Ok((block_import_params, Default::default()))

core/consensus/babe/src/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ fn propose_and_import_block(
578578
finalized: false,
579579
auxiliary: Vec::new(),
580580
fork_choice: ForkChoiceStrategy::LongestChain,
581+
allow_missing_state: false,
581582
},
582583
Default::default(),
583584
).unwrap();

core/consensus/common/src/block_import.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,15 @@ pub enum ImportResult {
3535
KnownBad,
3636
/// Block parent is not in the chain.
3737
UnknownParent,
38+
/// Parent state is missing.
39+
MissingState,
3840
}
3941

4042
/// Auxiliary data associated with an imported block result.
4143
#[derive(Debug, Default, PartialEq, Eq)]
4244
pub struct ImportedAux {
45+
/// Only the header has been imported. Block body verification was skipped.
46+
pub header_only: bool,
4347
/// Clear all pending justification requests.
4448
pub clear_justification_requests: bool,
4549
/// Request a justification for the given block.
@@ -98,6 +102,8 @@ pub struct BlockCheckParams<Block: BlockT> {
98102
pub number: NumberFor<Block>,
99103
/// Parent hash of the block that we verify.
100104
pub parent_hash: Block::Hash,
105+
/// Allow importing the block skipping state verification if parent state is missing.
106+
pub allow_missing_state: bool,
101107
}
102108

103109
/// Data required to import a Block.
@@ -133,6 +139,8 @@ pub struct BlockImportParams<Block: BlockT> {
133139
/// Fork choice strategy of this import. This should only be set by a
134140
/// synchronous import, otherwise it may race against other imports.
135141
pub fork_choice: ForkChoiceStrategy,
142+
/// Allow importing the block skipping state verification if parent state is missing.
143+
pub allow_missing_state: bool,
136144
}
137145

138146
impl<Block: BlockT> BlockImportParams<Block> {

0 commit comments

Comments
 (0)