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

Adds cli interface to allow reseting chain to a particular block #9782

Merged
merged 13 commits into from
Jan 16, 2019
15 changes: 15 additions & 0 deletions ethcore/blockchain/src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,21 @@ impl BlockChain {
self.db.key_value().read_with_cache(db::COL_EXTRA, &self.block_details, parent).map_or(false, |d| d.children.contains(hash))
}

/// fetches the list of blocks from best block to n, and n's parent hash
/// where n > 0
pub fn block_headers_from_best_block(&self, n: u32) -> Option<(Vec<encoded::Header>, H256)> {
let mut blocks = Vec::with_capacity(n as usize);
let mut hash = self.best_block_hash();

for _ in 0..n {
let current_hash = self.block_header_data(&hash)?;
hash = current_hash.parent_hash();
blocks.push(current_hash);
}

Some((blocks, hash))
}

/// Returns a tree route between `from` and `to`, which is a tuple of:
///
/// - a vector of hashes of all blocks, ordered from `from` to `to`.
Expand Down
2 changes: 1 addition & 1 deletion ethcore/blockchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ pub use self::cache::CacheSize;
pub use self::config::Config;
pub use self::import_route::ImportRoute;
pub use self::update::ExtrasInsert;
pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress};
pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey};
pub use common_types::tree_route::TreeRoute;
49 changes: 47 additions & 2 deletions ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::sync::{Arc, Weak};
use std::time::{Instant, Duration};

use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert};
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey};
use bytes::Bytes;
use ethcore_miner::pool::VerifiedTransaction;
use ethereum_types::{H256, Address, U256};
Expand Down Expand Up @@ -50,7 +50,7 @@ use client::{
RegistryInfo, ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
ClientIoMessage,
ClientIoMessage, BlockChainReset
};
use client::{
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
Expand All @@ -77,12 +77,14 @@ use verification::queue::kind::BlockLike;
use verification::queue::kind::blocks::Unverified;
use verification::{PreverifiedBlock, Verifier, BlockQueue};
use verification;
use ansi_term::Colour;

// re-export
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
pub use blockchain::CacheSize as BlockChainCacheSize;
pub use verification::QueueInfo as BlockQueueInfo;
use db::Writable;

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

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

impl BlockChainReset for Client {
fn reset(&self, num: u32) -> Result<(), String> {
if num as u64 > self.pruning_history() {
return Err("Attempting to reset to block with pruned state".into())
}

let (blocks_to_delete, best_block_hash) = self.chain.read()
.block_headers_from_best_block(num)
.ok_or("Attempted to reset past genesis block")?;

let mut db_transaction = DBTransaction::with_capacity((num + 1) as usize);

for hash in &blocks_to_delete {
seunlanlege marked this conversation as resolved.
Show resolved Hide resolved
db_transaction.delete(::db::COL_HEADERS, &hash.hash());
db_transaction.delete(::db::COL_BODIES, &hash.hash());
db_transaction.delete(::db::COL_EXTRA, &hash.hash());
Writable::delete::<H256, BlockNumberKey>
(&mut db_transaction, ::db::COL_EXTRA, &hash.number());
}

// update the new best block hash
db_transaction.put(::db::COL_EXTRA, b"best", &*best_block_hash);

self.db.read()
.key_value()
.write(db_transaction)
.map_err(|err| format!("could not complete reset operation; io error occured: {}", err))?;

let hashes = blocks_to_delete.iter().map(|b| b.hash()).collect::<Vec<_>>();

info!("Deleting block hashes {}",
Colour::Red
.bold()
.paint(format!("{:#?}", hashes))
);

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this should be either debug or trace instead of info. Can you explain why it is info?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm using info because i need to communicate to the user which blocks are being deleted.

Copy link
Member Author

Choose a reason for hiding this comment

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

the user shouldn't need to run parity with -l trace or debug to see it

info!("New best block hash {}", Colour::Green.bold().paint(format!("{:?}", best_block_hash)));

Ok(())
}
}

impl Nonce for Client {
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
self.state_at(id).and_then(|s| s.nonce(address).ok())
Expand Down
3 changes: 2 additions & 1 deletion ethcore/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType};
pub use self::traits::{
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock,
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks,
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not introduced by this PR, but those three lines should all use tab for indentation.

BadBlocks, BlockChainReset
};
pub use state::StateInfo;
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};
Expand Down
6 changes: 6 additions & 0 deletions ethcore/src/client/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,9 @@ pub trait ProvingBlockChainClient: BlockChainClient {
/// Get an epoch change signal by block hash.
fn epoch_signal(&self, hash: H256) -> Option<Vec<u8>>;
}

/// resets the blockchain
pub trait BlockChainReset {
/// reset to best_block - n
fn reset(&self, num: u32) -> Result<(), String>;
}
42 changes: 41 additions & 1 deletion parity/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use ethereum_types::{U256, H256, Address};
use bytes::ToPretty;
use rlp::PayloadInfo;
use ethcore::account_provider::AccountProvider;
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, Nonce, Balance, BlockChainClient, BlockId, BlockInfo, ImportBlock};
use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, Nonce, Balance, BlockChainClient, BlockId, BlockInfo,
ImportBlock, BlockChainReset};
use ethcore::error::{ImportErrorKind, ErrorKind as EthcoreErrorKind, Error as EthcoreError};
use ethcore::miner::Miner;
use ethcore::verification::queue::VerifierSettings;
Expand All @@ -40,6 +41,7 @@ use dir::Directories;
use user_defaults::UserDefaults;
use ethcore_private_tx;
use db;
use ansi_term::Colour;

#[derive(Debug, PartialEq)]
pub enum DataFormat {
Expand Down Expand Up @@ -71,6 +73,21 @@ pub enum BlockchainCmd {
Import(ImportBlockchain),
Export(ExportBlockchain),
ExportState(ExportState),
Reset(ResetBlockchain)
}

#[derive(Debug, PartialEq)]
pub struct ResetBlockchain {
pub dirs: Directories,
pub spec: SpecType,
pub pruning: Pruning,
pub pruning_history: u64,
pub pruning_memory: usize,
pub tracing: Switch,
pub fat_db: Switch,
pub compaction: DatabaseCompactionProfile,
pub cache_config: CacheConfig,
pub num: u32,
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -153,6 +170,7 @@ pub fn execute(cmd: BlockchainCmd) -> Result<(), String> {
}
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd),
BlockchainCmd::Reset(reset_cmd) => execute_reset(reset_cmd),
}
}

Expand Down Expand Up @@ -709,6 +727,28 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> {
Ok(())
}

fn execute_reset(cmd: ResetBlockchain) -> Result<(), String> {
let service = start_client(
cmd.dirs,
cmd.spec,
cmd.pruning,
cmd.pruning_history,
cmd.pruning_memory,
cmd.tracing,
cmd.fat_db,
cmd.compaction,
cmd.cache_config,
false,
0,
)?;

let client = service.client();
client.reset(cmd.num)?;
info!("{}", Colour::Green.bold().paint("Successfully reset db!"));

Ok(())
}

pub fn kill_db(cmd: KillBlockchain) -> Result<(), String> {
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
let genesis_hash = spec.genesis_header().hash();
Expand Down
11 changes: 11 additions & 0 deletions parity/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,15 @@ usage! {
CMD cmd_db_kill {
"Clean the database of the given --chain (default: mainnet)",
}

CMD cmd_db_reset {
"Removes NUM latests blocks from the db",

ARG arg_db_reset_num: (u32) = 10u32,
"<NUM>",
"Number of blocks to revert",
}

}

CMD cmd_export_hardcoded_sync
Expand Down Expand Up @@ -1612,6 +1621,7 @@ mod tests {
cmd_tools_hash: false,
cmd_db: false,
cmd_db_kill: false,
cmd_db_reset: false,
cmd_export_hardcoded_sync: false,

// Arguments
Expand All @@ -1631,6 +1641,7 @@ mod tests {
arg_dapp_path: None,
arg_account_import_path: None,
arg_wallet_import_path: None,
arg_db_reset_num: 10,

// -- Operating Options
arg_mode: "last".into(),
Expand Down
15 changes: 14 additions & 1 deletion parity/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use ethcore_private_tx::{ProviderConfig, EncryptorConfig};
use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress};
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
use run::RunCmd;
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat};
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat, ResetBlockchain};
use export_hardcoded_sync::ExportHsyncCmd;
use presale::ImportWallet;
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
Expand Down Expand Up @@ -176,6 +176,19 @@ impl Configuration {
}
} else if self.args.cmd_tools && self.args.cmd_tools_hash {
Cmd::Hash(self.args.arg_tools_hash_file)
} else if self.args.cmd_db && self.args.cmd_db_reset {
Cmd::Blockchain(BlockchainCmd::Reset(ResetBlockchain {
dirs,
spec,
pruning,
pruning_history,
pruning_memory: self.args.arg_pruning_memory,
tracing,
fat_db,
compaction,
cache_config,
num: self.args.arg_db_reset_num,
}))
} else if self.args.cmd_db && self.args.cmd_db_kill {
Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain {
spec: spec,
Expand Down