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

Commit cdba22a

Browse files
seunlanlege5chdn
authored andcommitted
Adds cli interface to allow reseting chain to a particular block (#9782)
* added BlockChainReset trait, client impl, and cli interface * show block hashes to be deleted and new best block, update best block in db, better cli interface * delete BlockNumber from COL_EXTRA * add TODO comment * add BlockReciepts to imports * refactor block_headers_from_best_block, better cli documentation * exit gracefully if reset arg isn't supplied * fix cli usage macro * removed stray int literals * use Vec::with_capacity Co-Authored-By: seunlanlege <seunlanlege@gmail.com> * cast n to usize * correct imports * make db reset arg required
1 parent 1df6361 commit cdba22a

File tree

8 files changed

+137
-6
lines changed

8 files changed

+137
-6
lines changed

ethcore/blockchain/src/blockchain.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,21 @@ impl BlockChain {
668668
self.db.key_value().read_with_cache(db::COL_EXTRA, &self.block_details, parent).map_or(false, |d| d.children.contains(hash))
669669
}
670670

671+
/// fetches the list of blocks from best block to n, and n's parent hash
672+
/// where n > 0
673+
pub fn block_headers_from_best_block(&self, n: u32) -> Option<(Vec<encoded::Header>, H256)> {
674+
let mut blocks = Vec::with_capacity(n as usize);
675+
let mut hash = self.best_block_hash();
676+
677+
for _ in 0..n {
678+
let current_hash = self.block_header_data(&hash)?;
679+
hash = current_hash.parent_hash();
680+
blocks.push(current_hash);
681+
}
682+
683+
Some((blocks, hash))
684+
}
685+
671686
/// Returns a tree route between `from` and `to`, which is a tuple of:
672687
///
673688
/// - a vector of hashes of all blocks, ordered from `from` to `to`.

ethcore/blockchain/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ pub use self::cache::CacheSize;
3333
pub use self::config::Config;
3434
pub use self::import_route::ImportRoute;
3535
pub use self::update::ExtrasInsert;
36-
pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress};
36+
pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey};
3737
pub use common_types::tree_route::TreeRoute;

ethcore/src/client/client.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
2121
use std::sync::{Arc, Weak};
2222
use std::time::{Instant, Duration};
2323

24-
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert};
24+
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey};
2525
use bytes::Bytes;
2626
use ethcore_miner::pool::VerifiedTransaction;
2727
use ethereum_types::{H256, Address, U256};
@@ -50,7 +50,7 @@ use client::{
5050
RegistryInfo, ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
5151
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
5252
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
53-
ClientIoMessage,
53+
ClientIoMessage, BlockChainReset
5454
};
5555
use client::{
5656
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
@@ -77,12 +77,14 @@ use verification::queue::kind::BlockLike;
7777
use verification::queue::kind::blocks::Unverified;
7878
use verification::{PreverifiedBlock, Verifier, BlockQueue};
7979
use verification;
80+
use ansi_term::Colour;
8081

8182
// re-export
8283
pub use types::blockchain_info::BlockChainInfo;
8384
pub use types::block_status::BlockStatus;
8485
pub use blockchain::CacheSize as BlockChainCacheSize;
8586
pub use verification::QueueInfo as BlockQueueInfo;
87+
use db::Writable;
8688

8789
use_contract!(registry, "res/contracts/registrar.json");
8890

@@ -469,6 +471,7 @@ impl Importer {
469471
// it is for reconstructing the state transition.
470472
//
471473
// The header passed is from the original block data and is sealed.
474+
// TODO: should return an error if ImportRoute is none, issue #9910
472475
fn commit_block<B>(&self, block: B, header: &Header, block_data: encoded::Block, client: &Client) -> ImportRoute where B: Drain {
473476
let hash = &header.hash();
474477
let number = header.number();
@@ -1328,6 +1331,48 @@ impl snapshot::DatabaseRestore for Client {
13281331
}
13291332
}
13301333

1334+
impl BlockChainReset for Client {
1335+
fn reset(&self, num: u32) -> Result<(), String> {
1336+
if num as u64 > self.pruning_history() {
1337+
return Err("Attempting to reset to block with pruned state".into())
1338+
}
1339+
1340+
let (blocks_to_delete, best_block_hash) = self.chain.read()
1341+
.block_headers_from_best_block(num)
1342+
.ok_or("Attempted to reset past genesis block")?;
1343+
1344+
let mut db_transaction = DBTransaction::with_capacity((num + 1) as usize);
1345+
1346+
for hash in &blocks_to_delete {
1347+
db_transaction.delete(::db::COL_HEADERS, &hash.hash());
1348+
db_transaction.delete(::db::COL_BODIES, &hash.hash());
1349+
db_transaction.delete(::db::COL_EXTRA, &hash.hash());
1350+
Writable::delete::<H256, BlockNumberKey>
1351+
(&mut db_transaction, ::db::COL_EXTRA, &hash.number());
1352+
}
1353+
1354+
// update the new best block hash
1355+
db_transaction.put(::db::COL_EXTRA, b"best", &*best_block_hash);
1356+
1357+
self.db.read()
1358+
.key_value()
1359+
.write(db_transaction)
1360+
.map_err(|err| format!("could not complete reset operation; io error occured: {}", err))?;
1361+
1362+
let hashes = blocks_to_delete.iter().map(|b| b.hash()).collect::<Vec<_>>();
1363+
1364+
info!("Deleting block hashes {}",
1365+
Colour::Red
1366+
.bold()
1367+
.paint(format!("{:#?}", hashes))
1368+
);
1369+
1370+
info!("New best block hash {}", Colour::Green.bold().paint(format!("{:?}", best_block_hash)));
1371+
1372+
Ok(())
1373+
}
1374+
}
1375+
13311376
impl Nonce for Client {
13321377
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
13331378
self.state_at(id).and_then(|s| s.nonce(address).ok())

ethcore/src/client/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ pub use self::test_client::{TestBlockChainClient, EachBlockWith};
3737
pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType};
3838
pub use self::traits::{
3939
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock,
40-
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks,
40+
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter,
41+
BadBlocks, BlockChainReset
4142
};
4243
pub use state::StateInfo;
4344
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};

ethcore/src/client/traits.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,3 +482,9 @@ pub trait ProvingBlockChainClient: BlockChainClient {
482482
/// Get an epoch change signal by block hash.
483483
fn epoch_signal(&self, hash: H256) -> Option<Vec<u8>>;
484484
}
485+
486+
/// resets the blockchain
487+
pub trait BlockChainReset {
488+
/// reset to best_block - n
489+
fn reset(&self, num: u32) -> Result<(), String>;
490+
}

parity/blockchain.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ use ethereum_types::{U256, H256, Address};
2626
use bytes::ToPretty;
2727
use rlp::PayloadInfo;
2828
use ethcore::account_provider::AccountProvider;
29-
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, Nonce, Balance, BlockChainClient, BlockId, BlockInfo, ImportBlock};
29+
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, Nonce, Balance, BlockChainClient, BlockId, BlockInfo,
30+
ImportBlock, BlockChainReset};
3031
use ethcore::error::{ImportErrorKind, ErrorKind as EthcoreErrorKind, Error as EthcoreError};
3132
use ethcore::miner::Miner;
3233
use ethcore::verification::queue::VerifierSettings;
@@ -40,6 +41,7 @@ use dir::Directories;
4041
use user_defaults::UserDefaults;
4142
use ethcore_private_tx;
4243
use db;
44+
use ansi_term::Colour;
4345

4446
#[derive(Debug, PartialEq)]
4547
pub enum DataFormat {
@@ -71,6 +73,21 @@ pub enum BlockchainCmd {
7173
Import(ImportBlockchain),
7274
Export(ExportBlockchain),
7375
ExportState(ExportState),
76+
Reset(ResetBlockchain)
77+
}
78+
79+
#[derive(Debug, PartialEq)]
80+
pub struct ResetBlockchain {
81+
pub dirs: Directories,
82+
pub spec: SpecType,
83+
pub pruning: Pruning,
84+
pub pruning_history: u64,
85+
pub pruning_memory: usize,
86+
pub tracing: Switch,
87+
pub fat_db: Switch,
88+
pub compaction: DatabaseCompactionProfile,
89+
pub cache_config: CacheConfig,
90+
pub num: u32,
7491
}
7592

7693
#[derive(Debug, PartialEq)]
@@ -153,6 +170,7 @@ pub fn execute(cmd: BlockchainCmd) -> Result<(), String> {
153170
}
154171
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
155172
BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd),
173+
BlockchainCmd::Reset(reset_cmd) => execute_reset(reset_cmd),
156174
}
157175
}
158176

@@ -709,6 +727,28 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> {
709727
Ok(())
710728
}
711729

730+
fn execute_reset(cmd: ResetBlockchain) -> Result<(), String> {
731+
let service = start_client(
732+
cmd.dirs,
733+
cmd.spec,
734+
cmd.pruning,
735+
cmd.pruning_history,
736+
cmd.pruning_memory,
737+
cmd.tracing,
738+
cmd.fat_db,
739+
cmd.compaction,
740+
cmd.cache_config,
741+
false,
742+
0,
743+
)?;
744+
745+
let client = service.client();
746+
client.reset(cmd.num)?;
747+
info!("{}", Colour::Green.bold().paint("Successfully reset db!"));
748+
749+
Ok(())
750+
}
751+
712752
pub fn kill_db(cmd: KillBlockchain) -> Result<(), String> {
713753
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
714754
let genesis_hash = spec.genesis_header().hash();

parity/cli/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,15 @@ usage! {
217217
CMD cmd_db_kill {
218218
"Clean the database of the given --chain (default: mainnet)",
219219
}
220+
221+
CMD cmd_db_reset {
222+
"Removes NUM latests blocks from the db",
223+
224+
ARG arg_db_reset_num: (u32) = 10u32,
225+
"<NUM>",
226+
"Number of blocks to revert",
227+
}
228+
220229
}
221230

222231
CMD cmd_export_hardcoded_sync
@@ -1612,6 +1621,7 @@ mod tests {
16121621
cmd_tools_hash: false,
16131622
cmd_db: false,
16141623
cmd_db_kill: false,
1624+
cmd_db_reset: false,
16151625
cmd_export_hardcoded_sync: false,
16161626

16171627
// Arguments
@@ -1631,6 +1641,7 @@ mod tests {
16311641
arg_dapp_path: None,
16321642
arg_account_import_path: None,
16331643
arg_wallet_import_path: None,
1644+
arg_db_reset_num: 10,
16341645

16351646
// -- Operating Options
16361647
arg_mode: "last".into(),

parity/configuration.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use ethcore_private_tx::{ProviderConfig, EncryptorConfig};
4848
use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress};
4949
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
5050
use run::RunCmd;
51-
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat};
51+
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat, ResetBlockchain};
5252
use export_hardcoded_sync::ExportHsyncCmd;
5353
use presale::ImportWallet;
5454
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
@@ -176,6 +176,19 @@ impl Configuration {
176176
}
177177
} else if self.args.cmd_tools && self.args.cmd_tools_hash {
178178
Cmd::Hash(self.args.arg_tools_hash_file)
179+
} else if self.args.cmd_db && self.args.cmd_db_reset {
180+
Cmd::Blockchain(BlockchainCmd::Reset(ResetBlockchain {
181+
dirs,
182+
spec,
183+
pruning,
184+
pruning_history,
185+
pruning_memory: self.args.arg_pruning_memory,
186+
tracing,
187+
fat_db,
188+
compaction,
189+
cache_config,
190+
num: self.args.arg_db_reset_num,
191+
}))
179192
} else if self.args.cmd_db && self.args.cmd_db_kill {
180193
Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain {
181194
spec: spec,

0 commit comments

Comments
 (0)