Light client improvements (#6156)
* no seal checking * import command and --no-seal-check for light client * fix eth_call * tweak registry dapps lookup * ignore failed requests to non-server peers
This commit is contained in:
committed by
Arkadiy Paronyan
parent
5eb8cea6e7
commit
70ef33f6fe
@@ -93,6 +93,7 @@ pub struct ImportBlockchain {
|
||||
pub check_seal: bool,
|
||||
pub with_color: bool,
|
||||
pub verifier_settings: VerifierSettings,
|
||||
pub light: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -138,12 +139,165 @@ pub struct ExportState {
|
||||
pub fn execute(cmd: BlockchainCmd) -> Result<(), String> {
|
||||
match cmd {
|
||||
BlockchainCmd::Kill(kill_cmd) => kill_db(kill_cmd),
|
||||
BlockchainCmd::Import(import_cmd) => execute_import(import_cmd),
|
||||
BlockchainCmd::Import(import_cmd) => {
|
||||
if import_cmd.light {
|
||||
execute_import_light(import_cmd)
|
||||
} else {
|
||||
execute_import(import_cmd)
|
||||
}
|
||||
}
|
||||
BlockchainCmd::Export(export_cmd) => execute_export(export_cmd),
|
||||
BlockchainCmd::ExportState(export_cmd) => execute_export_state(export_cmd),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> {
|
||||
use light::client::{Service as LightClientService, Config as LightClientConfig};
|
||||
use light::cache::Cache as LightDataCache;
|
||||
|
||||
let timer = Instant::now();
|
||||
|
||||
// load spec file
|
||||
let spec = cmd.spec.spec(&cmd.dirs.cache)?;
|
||||
|
||||
// load genesis hash
|
||||
let genesis_hash = spec.genesis_header().hash();
|
||||
|
||||
// database paths
|
||||
let db_dirs = cmd.dirs.database(genesis_hash, None, spec.data_dir.clone());
|
||||
|
||||
// user defaults path
|
||||
let user_defaults_path = db_dirs.user_defaults_path();
|
||||
|
||||
// load user defaults
|
||||
let user_defaults = UserDefaults::load(&user_defaults_path)?;
|
||||
|
||||
fdlimit::raise_fd_limit();
|
||||
|
||||
// select pruning algorithm
|
||||
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
|
||||
|
||||
// prepare client and snapshot paths.
|
||||
let client_path = db_dirs.client_path(algorithm);
|
||||
|
||||
// execute upgrades
|
||||
let compaction = cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path());
|
||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, compaction)?;
|
||||
|
||||
// create dirs used by parity
|
||||
cmd.dirs.create_dirs(false, false, false)?;
|
||||
|
||||
let cache = Arc::new(::util::Mutex::new(
|
||||
LightDataCache::new(Default::default(), ::time::Duration::seconds(0))
|
||||
));
|
||||
|
||||
let mut config = LightClientConfig {
|
||||
queue: Default::default(),
|
||||
chain_column: ::ethcore::db::COL_LIGHT_CHAIN,
|
||||
db_cache_size: Some(cmd.cache_config.blockchain() as usize * 1024 * 1024),
|
||||
db_compaction: compaction,
|
||||
db_wal: cmd.wal,
|
||||
verify_full: true,
|
||||
check_seal: cmd.check_seal,
|
||||
};
|
||||
|
||||
config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024;
|
||||
config.queue.verifier_settings = cmd.verifier_settings;
|
||||
|
||||
let service = LightClientService::start(config, &spec, &client_path, cache)
|
||||
.map_err(|e| format!("Failed to start client: {}", e))?;
|
||||
|
||||
// free up the spec in memory.
|
||||
drop(spec);
|
||||
|
||||
let client = service.client();
|
||||
|
||||
let mut instream: Box<io::Read> = match cmd.file_path {
|
||||
Some(f) => Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?),
|
||||
None => Box::new(io::stdin()),
|
||||
};
|
||||
|
||||
const READAHEAD_BYTES: usize = 8;
|
||||
|
||||
let mut first_bytes: Vec<u8> = vec![0; READAHEAD_BYTES];
|
||||
let mut first_read = 0;
|
||||
|
||||
let format = match cmd.format {
|
||||
Some(format) => format,
|
||||
None => {
|
||||
first_read = instream.read(&mut first_bytes).map_err(|_| "Error reading from the file/stream.")?;
|
||||
match first_bytes[0] {
|
||||
0xf9 => DataFormat::Binary,
|
||||
_ => DataFormat::Hex,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let do_import = |bytes: Vec<u8>| {
|
||||
while client.queue_info().is_full() { sleep(Duration::from_secs(1)); }
|
||||
|
||||
let header: ::ethcore::header::Header = ::rlp::UntrustedRlp::new(&bytes).val_at(0)
|
||||
.map_err(|e| format!("Bad block: {}", e))?;
|
||||
|
||||
if client.best_block_header().number() >= header.number() { return Ok(()) }
|
||||
|
||||
if header.number() % 10000 == 0 {
|
||||
info!("#{}", header.number());
|
||||
}
|
||||
|
||||
match client.import_header(header) {
|
||||
Err(BlockImportError::Import(ImportError::AlreadyInChain)) => {
|
||||
trace!("Skipping block already in chain.");
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("Cannot import block: {:?}", e));
|
||||
},
|
||||
Ok(_) => {},
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match format {
|
||||
DataFormat::Binary => {
|
||||
loop {
|
||||
let mut bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; READAHEAD_BYTES]};
|
||||
let n = if first_read > 0 {
|
||||
first_read
|
||||
} else {
|
||||
instream.read(&mut bytes).map_err(|_| "Error reading from the file/stream.")?
|
||||
};
|
||||
if n == 0 { break; }
|
||||
first_read = 0;
|
||||
let s = PayloadInfo::from(&bytes).map_err(|e| format!("Invalid RLP in the file/stream: {:?}", e))?.total();
|
||||
bytes.resize(s, 0);
|
||||
instream.read_exact(&mut bytes[n..]).map_err(|_| "Error reading from the file/stream.")?;
|
||||
do_import(bytes)?;
|
||||
}
|
||||
}
|
||||
DataFormat::Hex => {
|
||||
for line in BufReader::new(instream).lines() {
|
||||
let s = line.map_err(|_| "Error reading from the file/stream.")?;
|
||||
let s = if first_read > 0 {from_utf8(&first_bytes).unwrap().to_owned() + &(s[..])} else {s};
|
||||
first_read = 0;
|
||||
let bytes = s.from_hex().map_err(|_| "Invalid hex in file/stream.")?;
|
||||
do_import(bytes)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
client.flush_queue();
|
||||
|
||||
let ms = timer.elapsed().as_milliseconds();
|
||||
let report = client.report();
|
||||
|
||||
info!("Import completed in {} seconds, {} headers, {} hdr/s",
|
||||
ms / 1000,
|
||||
report.blocks_imported,
|
||||
(report.blocks_imported * 1000) as u64 / ms,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_import(cmd: ImportBlockchain) -> Result<(), String> {
|
||||
let timer = Instant::now();
|
||||
|
||||
|
||||
@@ -243,6 +243,7 @@ impl Configuration {
|
||||
check_seal: !self.args.flag_no_seal_check,
|
||||
with_color: logger_config.color,
|
||||
verifier_settings: self.verifier_settings(),
|
||||
light: self.args.flag_light,
|
||||
};
|
||||
Cmd::Blockchain(BlockchainCmd::Import(import_cmd))
|
||||
} else if self.args.cmd_export {
|
||||
@@ -1184,6 +1185,7 @@ mod tests {
|
||||
check_seal: true,
|
||||
with_color: !cfg!(windows),
|
||||
verifier_settings: Default::default(),
|
||||
light: false,
|
||||
})));
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ impl ContractClient for LightRegistrar {
|
||||
tx: Transaction {
|
||||
nonce: self.client.engine().account_start_nonce(header.number()),
|
||||
action: Action::Call(address),
|
||||
gas: 50_000_000.into(),
|
||||
gas: 50_000.into(), // should be enough for all registry lookups. TODO: exponential backoff
|
||||
gas_price: 0.into(),
|
||||
value: 0.into(),
|
||||
data: data,
|
||||
|
||||
@@ -211,6 +211,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
||||
db_compaction: compaction,
|
||||
db_wal: cmd.wal,
|
||||
verify_full: true,
|
||||
check_seal: cmd.check_seal,
|
||||
};
|
||||
|
||||
config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024;
|
||||
|
||||
Reference in New Issue
Block a user