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))
|
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:
|
/// 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`.
|
/// - 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::config::Config;
|
||||||
pub use self::import_route::ImportRoute;
|
pub use self::import_route::ImportRoute;
|
||||||
pub use self::update::ExtrasInsert;
|
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;
|
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::sync::{Arc, Weak};
|
||||||
use std::time::{Instant, Duration};
|
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 bytes::Bytes;
|
||||||
use ethcore_miner::pool::VerifiedTransaction;
|
use ethcore_miner::pool::VerifiedTransaction;
|
||||||
use ethereum_types::{H256, Address, U256};
|
use ethereum_types::{H256, Address, U256};
|
||||||
@ -50,7 +50,7 @@ use client::{
|
|||||||
RegistryInfo, ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
|
RegistryInfo, ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
|
||||||
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
|
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
|
||||||
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
|
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
|
||||||
ClientIoMessage,
|
ClientIoMessage, BlockChainReset
|
||||||
};
|
};
|
||||||
use client::{
|
use client::{
|
||||||
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
|
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
|
||||||
@ -77,12 +77,14 @@ use verification::queue::kind::BlockLike;
|
|||||||
use verification::queue::kind::blocks::Unverified;
|
use verification::queue::kind::blocks::Unverified;
|
||||||
use verification::{PreverifiedBlock, Verifier, BlockQueue};
|
use verification::{PreverifiedBlock, Verifier, BlockQueue};
|
||||||
use verification;
|
use verification;
|
||||||
|
use ansi_term::Colour;
|
||||||
|
|
||||||
// re-export
|
// re-export
|
||||||
pub use types::blockchain_info::BlockChainInfo;
|
pub use types::blockchain_info::BlockChainInfo;
|
||||||
pub use types::block_status::BlockStatus;
|
pub use types::block_status::BlockStatus;
|
||||||
pub use blockchain::CacheSize as BlockChainCacheSize;
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
pub use verification::QueueInfo as BlockQueueInfo;
|
pub use verification::QueueInfo as BlockQueueInfo;
|
||||||
|
use db::Writable;
|
||||||
|
|
||||||
use_contract!(registry, "res/contracts/registrar.json");
|
use_contract!(registry, "res/contracts/registrar.json");
|
||||||
|
|
||||||
@ -469,6 +471,7 @@ impl Importer {
|
|||||||
// it is for reconstructing the state transition.
|
// it is for reconstructing the state transition.
|
||||||
//
|
//
|
||||||
// The header passed is from the original block data and is sealed.
|
// 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 {
|
fn commit_block<B>(&self, block: B, header: &Header, block_data: encoded::Block, client: &Client) -> ImportRoute where B: Drain {
|
||||||
let hash = &header.hash();
|
let hash = &header.hash();
|
||||||
let number = header.number();
|
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 {
|
impl Nonce for Client {
|
||||||
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
|
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
|
||||||
self.state_at(id).and_then(|s| s.nonce(address).ok())
|
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::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType};
|
||||||
pub use self::traits::{
|
pub use self::traits::{
|
||||||
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock,
|
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 state::StateInfo;
|
||||||
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};
|
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};
|
||||||
|
@ -482,3 +482,9 @@ pub trait ProvingBlockChainClient: BlockChainClient {
|
|||||||
/// Get an epoch change signal by block hash.
|
/// Get an epoch change signal by block hash.
|
||||||
fn epoch_signal(&self, hash: H256) -> Option<Vec<u8>>;
|
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 bytes::ToPretty;
|
||||||
use rlp::PayloadInfo;
|
use rlp::PayloadInfo;
|
||||||
use ethcore::account_provider::AccountProvider;
|
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::error::{ImportErrorKind, ErrorKind as EthcoreErrorKind, Error as EthcoreError};
|
||||||
use ethcore::miner::Miner;
|
use ethcore::miner::Miner;
|
||||||
use ethcore::verification::queue::VerifierSettings;
|
use ethcore::verification::queue::VerifierSettings;
|
||||||
@ -40,6 +41,7 @@ use dir::Directories;
|
|||||||
use user_defaults::UserDefaults;
|
use user_defaults::UserDefaults;
|
||||||
use ethcore_private_tx;
|
use ethcore_private_tx;
|
||||||
use db;
|
use db;
|
||||||
|
use ansi_term::Colour;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DataFormat {
|
pub enum DataFormat {
|
||||||
@ -71,6 +73,21 @@ pub enum BlockchainCmd {
|
|||||||
Import(ImportBlockchain),
|
Import(ImportBlockchain),
|
||||||
Export(ExportBlockchain),
|
Export(ExportBlockchain),
|
||||||
ExportState(ExportState),
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -153,6 +170,7 @@ pub fn execute(cmd: BlockchainCmd) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
|
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
|
||||||
BlockchainCmd::ExportState(export_cmd) => execute_export_state(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(())
|
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> {
|
pub fn kill_db(cmd: KillBlockchain) -> Result<(), String> {
|
||||||
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
|
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
|
||||||
let genesis_hash = spec.genesis_header().hash();
|
let genesis_hash = spec.genesis_header().hash();
|
||||||
|
@ -217,6 +217,15 @@ usage! {
|
|||||||
CMD cmd_db_kill {
|
CMD cmd_db_kill {
|
||||||
"Clean the database of the given --chain (default: mainnet)",
|
"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
|
CMD cmd_export_hardcoded_sync
|
||||||
@ -1612,6 +1621,7 @@ mod tests {
|
|||||||
cmd_tools_hash: false,
|
cmd_tools_hash: false,
|
||||||
cmd_db: false,
|
cmd_db: false,
|
||||||
cmd_db_kill: false,
|
cmd_db_kill: false,
|
||||||
|
cmd_db_reset: false,
|
||||||
cmd_export_hardcoded_sync: false,
|
cmd_export_hardcoded_sync: false,
|
||||||
|
|
||||||
// Arguments
|
// Arguments
|
||||||
@ -1631,6 +1641,7 @@ mod tests {
|
|||||||
arg_dapp_path: None,
|
arg_dapp_path: None,
|
||||||
arg_account_import_path: None,
|
arg_account_import_path: None,
|
||||||
arg_wallet_import_path: None,
|
arg_wallet_import_path: None,
|
||||||
|
arg_db_reset_num: 10,
|
||||||
|
|
||||||
// -- Operating Options
|
// -- Operating Options
|
||||||
arg_mode: "last".into(),
|
arg_mode: "last".into(),
|
||||||
|
@ -48,7 +48,7 @@ use ethcore_private_tx::{ProviderConfig, EncryptorConfig};
|
|||||||
use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress};
|
use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress};
|
||||||
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
|
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
|
||||||
use run::RunCmd;
|
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 export_hardcoded_sync::ExportHsyncCmd;
|
||||||
use presale::ImportWallet;
|
use presale::ImportWallet;
|
||||||
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
|
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
|
||||||
@ -176,6 +176,19 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
} else if self.args.cmd_tools && self.args.cmd_tools_hash {
|
} else if self.args.cmd_tools && self.args.cmd_tools_hash {
|
||||||
Cmd::Hash(self.args.arg_tools_hash_file)
|
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 {
|
} else if self.args.cmd_db && self.args.cmd_db_kill {
|
||||||
Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain {
|
Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain {
|
||||||
spec: spec,
|
spec: spec,
|
||||||
|
Loading…
Reference in New Issue
Block a user