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
This commit is contained in:
		
							parent
							
								
									1df6361753
								
							
						
					
					
						commit
						cdba22a2cb
					
				@ -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`.
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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};
 | 
			
		||||
@ -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,
 | 
			
		||||
@ -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");
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
@ -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 {
 | 
			
		||||
			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))
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		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())
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
	BadBlocks, BlockChainReset
 | 
			
		||||
};
 | 
			
		||||
pub use state::StateInfo;
 | 
			
		||||
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};
 | 
			
		||||
 | 
			
		||||
@ -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>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -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 {
 | 
			
		||||
@ -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)]
 | 
			
		||||
@ -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),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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(),
 | 
			
		||||
 | 
			
		||||
@ -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};
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user