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

Commit aac8c45

Browse files
arkpargavofyork
authored andcommitted
Revert chain command (#393)
* Revert chain command * BLOCKS -> NUM * Fixed warning
1 parent 69b6afa commit aac8c45

File tree

11 files changed

+203
-29
lines changed

11 files changed

+203
-29
lines changed

polkadot/cli/src/cli.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,20 @@ subcommands:
178178
long: max-heap-pages
179179
value_name: COUNT
180180
help: The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing.
181+
- revert:
182+
about: Revert chain to the previous state
183+
args:
184+
- NUM:
185+
index: 1
186+
help: Number of blocks to revert. Default is 256.
187+
- chain:
188+
long: chain
189+
value_name: CHAIN_SPEC
190+
help: Specify the chain specification.
191+
takes_value: true
192+
- base-path:
193+
long: base-path
194+
short: d
195+
value_name: PATH
196+
help: Specify custom base path.
197+
takes_value: true

polkadot/cli/src/lib.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ pub fn run<I, T, W>(args: I, worker: W) -> error::Result<()> where
223223
return import_blocks(matches, worker.exit_only());
224224
}
225225

226+
if let Some(matches) = matches.subcommand_matches("revert") {
227+
return revert_chain(matches);
228+
}
229+
226230
let (spec, is_global) = load_spec(&matches)?;
227231
let mut config = service::Configuration::default_with_spec(spec);
228232

@@ -248,7 +252,7 @@ pub fn run<I, T, W>(args: I, worker: W) -> error::Result<()> where
248252

249253
config.pruning = match matches.value_of("pruning") {
250254
Some("archive") => PruningMode::ArchiveAll,
251-
None => PruningMode::keep_blocks(256),
255+
None => PruningMode::default(),
252256
Some(s) => PruningMode::keep_blocks(s.parse()
253257
.map_err(|_| error::ErrorKind::Input("Invalid pruning mode specified".to_owned()))?),
254258
};
@@ -495,6 +499,25 @@ fn import_blocks<E>(matches: &clap::ArgMatches, exit: E) -> error::Result<()>
495499
Ok(())
496500
}
497501

502+
fn revert_chain(matches: &clap::ArgMatches) -> error::Result<()> {
503+
let (spec, _) = load_spec(&matches)?;
504+
let base_path = base_path(matches);
505+
let mut config = service::Configuration::default_with_spec(spec);
506+
config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into();
507+
508+
let client = service::new_client(config)?;
509+
510+
let blocks = match matches.value_of("NUM") {
511+
Some(v) => v.parse().map_err(|_| "Invalid block count specified")?,
512+
None => 256,
513+
};
514+
515+
let reverted = client.revert(blocks)?;
516+
let info = client.info()?.chain;
517+
info!("Reverted {} blocks. Best: #{} ({})", reverted, info.best_number, info.best_hash);
518+
Ok(())
519+
}
520+
498521
fn run_until_exit<C, W>(
499522
runtime: &mut Runtime,
500523
service: service::Service<C>,

substrate/client/db/src/lib.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use parking_lot::RwLock;
5050
use primitives::H256;
5151
use runtime_primitives::generic::BlockId;
5252
use runtime_primitives::bft::Justification;
53-
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, Hash, HashFor, Zero};
53+
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, Hash, HashFor, NumberFor, Zero};
5454
use runtime_primitives::BuildStorage;
5555
use state_machine::backend::Backend as StateBackend;
5656
use executor::RuntimeInfo;
@@ -372,7 +372,12 @@ impl<Block: BlockT> client::backend::Backend<Block> for Backend<Block> {
372372
let finalizing_hash = if self.finalization_window == 0 {
373373
Some(hash)
374374
} else {
375-
self.blockchain.hash(As::sa((number_u64 - self.finalization_window) as u64))?
375+
let finalizing = number_u64 - self.finalization_window;
376+
if finalizing > self.storage.state_db.best_finalized() {
377+
self.blockchain.hash(As::sa(finalizing))?
378+
} else {
379+
None
380+
}
376381
};
377382
if let Some(finalizing_hash) = finalizing_hash {
378383
trace!("Finalizing block #{} ({:?})", number_u64 - self.finalization_window, finalizing_hash);
@@ -381,13 +386,43 @@ impl<Block: BlockT> client::backend::Backend<Block> for Backend<Block> {
381386
}
382387
}
383388

384-
debug!("DB Commit {:?} ({})", hash, number);
389+
debug!("DB Commit {:?} ({}), best = {}", hash, number, pending_block.is_best);
385390
self.storage.db.write(transaction).map_err(db_err)?;
386391
self.blockchain.update_meta(hash, number, pending_block.is_best);
387392
}
388393
Ok(())
389394
}
390395

396+
fn revert(&self, n: NumberFor<Block>) -> Result<NumberFor<Block>, client::error::Error> {
397+
use client::blockchain::HeaderBackend;
398+
let mut best = self.blockchain.info()?.best_number;
399+
for c in 0 .. n.as_() {
400+
if best == As::sa(0) {
401+
return Ok(As::sa(c))
402+
}
403+
let mut transaction = DBTransaction::new();
404+
match self.storage.state_db.revert_one() {
405+
Some(commit) => {
406+
apply_state_commit(&mut transaction, commit);
407+
let removed = self.blockchain.hash(best)?.ok_or_else(
408+
|| client::error::ErrorKind::UnknownBlock(
409+
format!("Error reverting to {}. Block hash not found.", best)))?;
410+
best -= As::sa(1);
411+
let key = number_to_db_key(best.clone());
412+
let hash = self.blockchain.hash(best)?.ok_or_else(
413+
|| client::error::ErrorKind::UnknownBlock(
414+
format!("Error reverting to {}. Block hash not found.", best)))?;
415+
transaction.put(columns::META, meta_keys::BEST_BLOCK, &key);
416+
transaction.delete(columns::BLOCK_INDEX, removed.as_ref());
417+
self.storage.db.write(transaction).map_err(db_err)?;
418+
self.blockchain.update_meta(hash, best, true);
419+
}
420+
None => return Ok(As::sa(c))
421+
}
422+
}
423+
Ok(n)
424+
}
425+
391426
fn blockchain(&self) -> &BlockchainDb<Block> {
392427
&self.blockchain
393428
}

substrate/client/src/backend.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
use state_machine::backend::Backend as StateBackend;
2020
use error;
2121
use runtime_primitives::bft::Justification;
22-
use runtime_primitives::traits::Block as BlockT;
22+
use runtime_primitives::traits::{Block as BlockT, NumberFor};
2323
use runtime_primitives::generic::BlockId;
2424

2525
/// Block insertion operation. Keeps hold if the inserted block state and data.
@@ -69,6 +69,9 @@ pub trait Backend<Block: BlockT>: Send + Sync {
6969
fn blockchain(&self) -> &Self::Blockchain;
7070
/// Returns state backend with post-state of given block.
7171
fn state_at(&self, block: BlockId<Block>) -> error::Result<Self::State>;
72+
/// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were
73+
/// successfully reverted.
74+
fn revert(&self, n: NumberFor<Block>) -> error::Result<NumberFor<Block>>;
7275
}
7376

7477
/// Mark for all Backend implementations, that are making use of state data, stored locally.

substrate/client/src/client.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use futures::sync::mpsc;
2121
use parking_lot::{Mutex, RwLock};
2222
use primitives::AuthorityId;
2323
use runtime_primitives::{bft::Justification, generic::{BlockId, SignedBlock, Block as RuntimeBlock}};
24-
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, As};
24+
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, As, NumberFor};
2525
use runtime_primitives::BuildStorage;
2626
use primitives::storage::{StorageKey, StorageData};
2727
use codec::Decode;
@@ -379,6 +379,12 @@ impl<B, E, Block> Client<B, E, Block> where
379379
Ok(ImportResult::Queued)
380380
}
381381

382+
/// Attempts to revert the chain by `n` blocks. Returns the number of blocks that were
383+
/// successfully reverted.
384+
pub fn revert(&self, n: NumberFor<Block>) -> error::Result<NumberFor<Block>> {
385+
Ok(self.backend.revert(n)?)
386+
}
387+
382388
/// Get blockchain info.
383389
pub fn info(&self) -> error::Result<ClientInfo<Block>> {
384390
let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?;

substrate/client/src/in_mem.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use error;
2323
use backend;
2424
use light;
2525
use runtime_primitives::generic::BlockId;
26-
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero};
26+
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor, As};
2727
use runtime_primitives::bft::Justification;
2828
use blockchain::{self, BlockStatus};
2929
use state_machine::backend::{Backend as StateBackend, InMemory};
@@ -309,6 +309,10 @@ impl<Block> backend::Backend<Block> for Backend<Block> where
309309
None => Err(error::ErrorKind::UnknownBlock(format!("{}", block)).into()),
310310
}
311311
}
312+
313+
fn revert(&self, _n: NumberFor<Block>) -> error::Result<NumberFor<Block>> {
314+
Ok(As::sa(0))
315+
}
312316
}
313317

314318
impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> {}

substrate/client/src/light/backend.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
use std::sync::{Arc, Weak};
2121

2222
use runtime_primitives::{bft::Justification, generic::BlockId};
23-
use runtime_primitives::traits::Block as BlockT;
23+
use runtime_primitives::traits::{Block as BlockT, NumberFor};
2424
use state_machine::{Backend as StateBackend, TrieBackend as StateTrieBackend,
2525
TryIntoTrieBackend as TryIntoStateTrieBackend};
2626

@@ -93,6 +93,10 @@ impl<S, F, Block> ClientBackend<Block> for Backend<S, F> where Block: BlockT, S:
9393
fetcher: self.blockchain.fetcher(),
9494
})
9595
}
96+
97+
fn revert(&self, _n: NumberFor<Block>) -> ClientResult<NumberFor<Block>> {
98+
unimplemented!()
99+
}
96100
}
97101

98102
impl<S, F, Block> RemoteBackend<Block> for Backend<S, F> where Block: BlockT, S: BlockchainStorage<Block>, F: Fetcher<Block> {}

substrate/network/src/import_queue.rs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ use std::sync::{Arc, Weak};
2121
use std::sync::atomic::{AtomicBool, Ordering};
2222
use parking_lot::{Condvar, Mutex, RwLock};
2323

24-
use client::{BlockOrigin, BlockStatus, ImportResult};
24+
use client::{BlockOrigin, ImportResult};
2525
use network_libp2p::{NodeIndex, Severity};
26-
use runtime_primitives::generic::BlockId;
26+
2727
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
2828

2929
use blocks::BlockData;
@@ -221,8 +221,6 @@ enum SyncLink<'a, B: 'a + BlockT, E: 'a + ExecuteInContext<B>> {
221221
/// Block import successful result.
222222
#[derive(Debug, PartialEq)]
223223
enum BlockImportResult<H: ::std::fmt::Debug + PartialEq, N: ::std::fmt::Debug + PartialEq> {
224-
/// Block is not imported.
225-
NotImported(H, N),
226224
/// Imported known block.
227225
ImportedKnown(H, N),
228226
/// Imported unknown block.
@@ -286,16 +284,6 @@ fn import_single_block<B: BlockT>(
286284
let hash = header.hash();
287285
let parent = header.parent_hash().clone();
288286

289-
// check whether the block is known before importing.
290-
match chain.block_status(&BlockId::Hash(hash)) {
291-
Ok(BlockStatus::InChain) => return Ok(BlockImportResult::NotImported(hash, number)),
292-
Ok(_) => {},
293-
Err(e) => {
294-
debug!(target: "sync", "Error importing block {}: {:?}: {:?}", number, hash, e);
295-
return Err(BlockImportError::Restart);
296-
}
297-
}
298-
299287
let result = chain.import(
300288
block_origin,
301289
header,
@@ -347,10 +335,9 @@ fn process_import_result<'a, B: BlockT>(
347335
) -> usize
348336
{
349337
match result {
350-
Ok(BlockImportResult::NotImported(_, _)) => 0,
351338
Ok(BlockImportResult::ImportedKnown(hash, number)) => {
352339
link.block_imported(&hash, number);
353-
0
340+
1
354341
},
355342
Ok(BlockImportResult::ImportedUnknown(hash, number)) => {
356343
link.block_imported(&hash, number);
@@ -431,6 +418,7 @@ pub mod tests {
431418
use test_client::{self, TestClient};
432419
use test_client::runtime::{Block, Hash};
433420
use on_demand::tests::DummyExecutor;
421+
use runtime_primitives::generic::BlockId;
434422
use super::*;
435423

436424
/// Blocks import queue that is importing blocks in the same thread.
@@ -522,7 +510,7 @@ pub mod tests {
522510
#[test]
523511
fn import_single_good_known_block_is_ignored() {
524512
let (client, hash, number, block) = prepare_good_block();
525-
assert_eq!(import_single_block(&client, BlockOrigin::File, block), Ok(BlockImportResult::NotImported(hash, number)));
513+
assert_eq!(import_single_block(&client, BlockOrigin::File, block), Ok(BlockImportResult::ImportedKnown(hash, number)));
526514
}
527515

528516
#[test]
@@ -542,11 +530,11 @@ pub mod tests {
542530
#[test]
543531
fn process_import_result_works() {
544532
let mut link = TestLink::new();
545-
assert_eq!(process_import_result::<Block>(&mut link, Ok(BlockImportResult::NotImported(Default::default(), 0))), 0);
546-
assert_eq!(link.total(), 0);
533+
assert_eq!(process_import_result::<Block>(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1);
534+
assert_eq!(link.total(), 1);
547535

548536
let mut link = TestLink::new();
549-
assert_eq!(process_import_result::<Block>(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 0);
537+
assert_eq!(process_import_result::<Block>(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1);
550538
assert_eq!(link.total(), 1);
551539
assert_eq!(link.imported, 1);
552540

substrate/service/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
7171
keys: Default::default(),
7272
custom: Default::default(),
7373
telemetry: Default::default(),
74-
pruning: PruningMode::ArchiveAll,
74+
pruning: PruningMode::default(),
7575
execution_strategy: ExecutionStrategy::Both,
7676
min_heap_pages: 8,
7777
max_heap_pages: 1024,

substrate/state-db/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ impl PruningMode {
138138
}
139139
}
140140

141+
impl Default for PruningMode {
142+
fn default() -> Self {
143+
PruningMode::keep_blocks(256)
144+
}
145+
}
146+
141147
fn to_meta_key<S: Codec>(suffix: &[u8], data: &S) -> Vec<u8> {
142148
let mut buffer = data.encode();
143149
buffer.extend(suffix);
@@ -216,6 +222,10 @@ impl<BlockHash: Hash, Key: Hash> StateDbSync<BlockHash, Key> {
216222
commit
217223
}
218224

225+
pub fn best_finalized(&self) -> u64 {
226+
return self.unfinalized.last_finalized_block_number()
227+
}
228+
219229
fn prune(&mut self, commit: &mut CommitSet<Key>) {
220230
if let (&mut Some(ref mut pruning), &PruningMode::Constrained(ref constraints)) = (&mut self.pruning, &self.mode) {
221231
loop {
@@ -237,6 +247,20 @@ impl<BlockHash: Hash, Key: Hash> StateDbSync<BlockHash, Key> {
237247
}
238248
}
239249

250+
/// Revert all unfinalized blocks with the best block number.
251+
/// Returns a database commit or `None` if not possible.
252+
/// For archive an empty commit set is returned.
253+
pub fn revert_one(&mut self) -> Option<CommitSet<Key>> {
254+
match self.mode {
255+
PruningMode::ArchiveAll => {
256+
Some(CommitSet::default())
257+
},
258+
PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => {
259+
self.unfinalized.revert_one()
260+
},
261+
}
262+
}
263+
240264
pub fn pin(&mut self, hash: &BlockHash) {
241265
self.pinned.insert(hash.clone());
242266
}
@@ -291,6 +315,19 @@ impl<BlockHash: Hash, Key: Hash> StateDb<BlockHash, Key> {
291315
pub fn get<D: HashDb<Hash=Key>>(&self, key: &Key, db: &D) -> Result<Option<DBValue>, Error<D::Error>> {
292316
self.db.read().get(key, db)
293317
}
318+
319+
/// Revert all unfinalized blocks with the best block number.
320+
/// Returns a database commit or `None` if not possible.
321+
/// For archive an empty commit set is returned.
322+
pub fn revert_one(&self) -> Option<CommitSet<Key>> {
323+
self.db.write().revert_one()
324+
}
325+
326+
/// Returns last finalized block number.
327+
pub fn best_finalized(&self) -> u64 {
328+
return self.db.read().best_finalized()
329+
}
330+
294331
}
295332

296333
#[cfg(test)]

0 commit comments

Comments
 (0)