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

Commit 48d6cbd

Browse files
seunlanlegedvdplm
authored andcommitted
Add new line after writing block to hex file. (#10984)
* add new line after writing block to hex file. * refactor for testability * correct import * better error reporting, code formatting * multiline imports * docs * better docs, move type to common types, merge ImportBlocks and ExportBlocks * tabs over spaces * correct test imports * Apply suggestions from code review Co-Authored-By: David <dvdplm@gmail.com> * correct typo * fixed test import
1 parent 46b5b50 commit 48d6cbd

File tree

9 files changed

+327
-128
lines changed

9 files changed

+327
-128
lines changed

ethcore/blockchain/src/blockchain.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ impl BlockChain {
592592
let best_block_rlp = bc.block(&best_block_hash)
593593
.expect("Best block is from a known block hash; qed");
594594

595-
// and write them
595+
// and write them to the cache.
596596
let mut best_block = bc.best_block.write();
597597
*best_block = BestBlock {
598598
total_difficulty: best_block_total_difficulty,
@@ -858,12 +858,31 @@ impl BlockChain {
858858
}
859859
}
860860

861-
/// clears all caches for testing purposes
861+
/// clears all caches, re-loads best block from disk for testing purposes
862862
pub fn clear_cache(&self) {
863863
self.block_bodies.write().clear();
864864
self.block_details.write().clear();
865865
self.block_hashes.write().clear();
866866
self.block_headers.write().clear();
867+
// Fetch best block details from disk
868+
let best_block_hash = self.db.key_value().get(db::COL_EXTRA, b"best")
869+
.expect("Low-level database error when fetching 'best' block. Some issue with disk?")
870+
.as_ref()
871+
.map(|r| H256::from_slice(r))
872+
.unwrap();
873+
let best_block_total_difficulty = self.block_details(&best_block_hash)
874+
.expect("Best block is from a known block hash; a known block hash always comes with a known block detail; qed")
875+
.total_difficulty;
876+
let best_block_rlp = self.block(&best_block_hash)
877+
.expect("Best block is from a known block hash; qed");
878+
879+
// and write them to the cache
880+
let mut best_block = self.best_block.write();
881+
*best_block = BestBlock {
882+
total_difficulty: best_block_total_difficulty,
883+
header: best_block_rlp.decode_header(),
884+
block: best_block_rlp,
885+
};
867886
}
868887

869888
/// Update the best ancient block to the given hash, after checking that

ethcore/src/client/client.rs

Lines changed: 140 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616

1717
use std::cmp;
1818
use std::collections::{HashSet, BTreeMap, VecDeque};
19-
use std::str::FromStr;
19+
use std::io::{BufRead, BufReader};
20+
use std::str::{FromStr, from_utf8};
2021
use std::convert::TryFrom;
2122
use std::sync::atomic::{AtomicI64, AtomicBool, Ordering as AtomicOrdering};
2223
use std::sync::{Arc, Weak};
2324
use std::time::{Instant, Duration};
2425

2526
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey};
26-
use bytes::Bytes;
27+
use bytes::{Bytes, ToPretty};
2728
use call_contract::{CallContract, RegistryInfo};
2829
use ethcore_miner::pool::VerifiedTransaction;
2930
use ethereum_types::{H256, H264, Address, U256};
@@ -35,6 +36,8 @@ use journaldb;
3536
use kvdb::{DBValue, KeyValueDB, DBTransaction};
3637
use parking_lot::{Mutex, RwLock};
3738
use rand::OsRng;
39+
use rlp::PayloadInfo;
40+
use rustc_hex::FromHex;
3841
use types::transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action};
3942
use trie::{TrieSpec, TrieFactory, Trie};
4043
use types::ancestry_action::AncestryAction;
@@ -43,6 +46,7 @@ use types::filter::Filter;
4346
use types::log_entry::LocalizedLogEntry;
4447
use types::receipt::{Receipt, LocalizedReceipt};
4548
use types::{BlockNumber, header::{Header, ExtendedHeader}};
49+
use types::data_format::DataFormat;
4650
use vm::{EnvInfo, LastHashes};
4751

4852
use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock};
@@ -52,7 +56,7 @@ use client::{
5256
ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
5357
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
5458
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
55-
ClientIoMessage, BlockChainReset
59+
ClientIoMessage, BlockChainReset, ImportExportBlocks,
5660
};
5761
use client::{
5862
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
@@ -64,7 +68,7 @@ use client::bad_blocks;
6468
use engines::{MAX_UNCLE_AGE, EthEngine, EpochTransition, ForkChoice, EngineError};
6569
use engines::epoch::PendingTransition;
6670
use error::{
67-
ImportErrorKind, ExecutionError, CallError, BlockError,
71+
ImportErrorKind, ExecutionError, CallError, BlockError, ImportError,
6872
QueueError, QueueErrorKind, Error as EthcoreError, EthcoreResult, ErrorKind as EthcoreErrorKind
6973
};
7074
use executive::{Executive, Executed, TransactOptions, contract_address};
@@ -154,7 +158,7 @@ struct Importer {
154158
pub import_lock: Mutex<()>, // FIXME Maybe wrap the whole `Importer` instead?
155159

156160
/// Used to verify blocks
157-
pub verifier: Box<Verifier<Client>>,
161+
pub verifier: Box<dyn Verifier<Client>>,
158162

159163
/// Queue containing pending blocks
160164
pub block_queue: BlockQueue,
@@ -197,7 +201,7 @@ pub struct Client {
197201
pruning: journaldb::Algorithm,
198202

199203
/// Client uses this to store blocks, traces, etc.
200-
db: RwLock<Arc<BlockChainDB>>,
204+
db: RwLock<Arc<dyn BlockChainDB>>,
201205

202206
state_db: RwLock<StateDB>,
203207

@@ -211,7 +215,7 @@ pub struct Client {
211215
io_channel: RwLock<IoChannel<ClientIoMessage>>,
212216

213217
/// List of actors to be notified on certain chain events
214-
notify: RwLock<Vec<Weak<ChainNotify>>>,
218+
notify: RwLock<Vec<Weak<dyn ChainNotify>>>,
215219

216220
/// Queued transactions from IO
217221
queue_transactions: IoChannelQueue,
@@ -233,12 +237,12 @@ pub struct Client {
233237
history: u64,
234238

235239
/// An action to be done if a mode/spec_name change happens
236-
on_user_defaults_change: Mutex<Option<Box<FnMut(Option<Mode>) + 'static + Send>>>,
240+
on_user_defaults_change: Mutex<Option<Box<dyn FnMut(Option<Mode>) + 'static + Send>>>,
237241

238242
registrar_address: Option<Address>,
239243

240244
/// A closure to call when we want to restart the client
241-
exit_handler: Mutex<Option<Box<Fn(String) + 'static + Send>>>,
245+
exit_handler: Mutex<Option<Box<dyn Fn(String) + 'static + Send>>>,
242246

243247
importer: Importer,
244248
}
@@ -249,8 +253,13 @@ impl Importer {
249253
engine: Arc<EthEngine>,
250254
message_channel: IoChannel<ClientIoMessage>,
251255
miner: Arc<Miner>,
252-
) -> Result<Importer, ::error::Error> {
253-
let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal());
256+
) -> Result<Importer, EthcoreError> {
257+
let block_queue = BlockQueue::new(
258+
config.queue.clone(),
259+
engine.clone(),
260+
message_channel.clone(),
261+
config.verifier_type.verifying_seal()
262+
);
254263

255264
Ok(Importer {
256265
import_lock: Mutex::new(()),
@@ -476,7 +485,16 @@ impl Importer {
476485
//
477486
// The header passed is from the original block data and is sealed.
478487
// TODO: should return an error if ImportRoute is none, issue #9910
479-
fn commit_block<B>(&self, block: B, header: &Header, block_data: encoded::Block, pending: Option<PendingTransition>, client: &Client) -> ImportRoute where B: Drain {
488+
fn commit_block<B>(
489+
&self,
490+
block: B,
491+
header: &Header,
492+
block_data: encoded::Block,
493+
pending: Option<PendingTransition>,
494+
client: &Client
495+
) -> ImportRoute
496+
where B: Drain
497+
{
480498
let hash = &header.hash();
481499
let number = header.number();
482500
let parent = header.parent_hash();
@@ -2561,6 +2579,116 @@ impl ProvingBlockChainClient for Client {
25612579

25622580
impl SnapshotClient for Client {}
25632581

2582+
impl ImportExportBlocks for Client {
2583+
fn export_blocks<'a>(
2584+
&self,
2585+
mut out: Box<dyn std::io::Write + 'a>,
2586+
from: BlockId,
2587+
to: BlockId,
2588+
format: Option<DataFormat>
2589+
) -> Result<(), String> {
2590+
let from = self.block_number(from).ok_or("Starting block could not be found")?;
2591+
let to = self.block_number(to).ok_or("End block could not be found")?;
2592+
let format = format.unwrap_or_default();
2593+
2594+
for i in from..=to {
2595+
if i % 10000 == 0 {
2596+
info!("#{}", i);
2597+
}
2598+
let b = self.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")?.into_inner();
2599+
match format {
2600+
DataFormat::Binary => {
2601+
out.write(&b).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?;
2602+
}
2603+
DataFormat::Hex => {
2604+
out.write_fmt(format_args!("{}\n", b.pretty())).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?;
2605+
}
2606+
}
2607+
}
2608+
Ok(())
2609+
}
2610+
2611+
fn import_blocks<'a>(
2612+
&self,
2613+
mut source: Box<dyn std::io::Read + 'a>,
2614+
format: Option<DataFormat>
2615+
) -> Result<(), String> {
2616+
const READAHEAD_BYTES: usize = 8;
2617+
2618+
let mut first_bytes: Vec<u8> = vec![0; READAHEAD_BYTES];
2619+
let mut first_read = 0;
2620+
2621+
let format = match format {
2622+
Some(format) => format,
2623+
None => {
2624+
first_read = source.read(&mut first_bytes).map_err(|_| "Error reading from the file/stream.")?;
2625+
match first_bytes[0] {
2626+
0xf9 => DataFormat::Binary,
2627+
_ => DataFormat::Hex,
2628+
}
2629+
}
2630+
};
2631+
2632+
let do_import = |bytes: Vec<u8>| {
2633+
let block = Unverified::from_rlp(bytes).map_err(|_| "Invalid block rlp")?;
2634+
let number = block.header.number();
2635+
while self.queue_info().is_full() { std::thread::sleep(Duration::from_secs(1)); }
2636+
match self.import_block(block) {
2637+
Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => {
2638+
trace!("Skipping block #{}: already in chain.", number);
2639+
},
2640+
Err(e) => {
2641+
return Err(format!("Cannot import block #{}: {:?}", number, e));
2642+
},
2643+
Ok(_) => {},
2644+
}
2645+
Ok(())
2646+
};
2647+
2648+
match format {
2649+
DataFormat::Binary => {
2650+
loop {
2651+
let (mut bytes, n) = if first_read > 0 {
2652+
(first_bytes.clone(), first_read)
2653+
} else {
2654+
let mut bytes = vec![0; READAHEAD_BYTES];
2655+
let n = source.read(&mut bytes)
2656+
.map_err(|err| format!("Error reading from the file/stream: {:?}", err))?;
2657+
(bytes, n)
2658+
};
2659+
if n == 0 { break; }
2660+
first_read = 0;
2661+
let s = PayloadInfo::from(&bytes)
2662+
.map_err(|e| format!("Invalid RLP in the file/stream: {:?}", e))?.total();
2663+
bytes.resize(s, 0);
2664+
source.read_exact(&mut bytes[n..])
2665+
.map_err(|err| format!("Error reading from the file/stream: {:?}", err))?;
2666+
do_import(bytes)?;
2667+
}
2668+
}
2669+
DataFormat::Hex => {
2670+
for line in BufReader::new(source).lines() {
2671+
let s = line
2672+
.map_err(|err| format!("Error reading from the file/stream: {:?}", err))?;
2673+
let s = if first_read > 0 {
2674+
from_utf8(&first_bytes)
2675+
.map_err(|err| format!("Invalid UTF-8: {:?}", err))?
2676+
.to_owned() + &(s[..])
2677+
} else {
2678+
s
2679+
};
2680+
first_read = 0;
2681+
let bytes = s.from_hex()
2682+
.map_err(|err| format!("Invalid hex in file/stream: {:?}", err))?;
2683+
do_import(bytes)?;
2684+
}
2685+
}
2686+
};
2687+
self.flush_queue();
2688+
Ok(())
2689+
}
2690+
}
2691+
25642692
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
25652693
/// and a vector of receipts from given block up to transaction index.
25662694
fn transaction_receipt(

ethcore/src/client/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType,
3838
pub use self::traits::{
3939
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, TransactionInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock,
4040
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks,
41-
BlockChainReset
41+
BlockChainReset, ImportExportBlocks
4242
};
4343
pub use state::StateInfo;
4444
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};

ethcore/src/client/traits.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use types::basic_account::BasicAccount;
3131
use types::block_status::BlockStatus;
3232
use types::blockchain_info::BlockChainInfo;
3333
use types::call_analytics::CallAnalytics;
34+
use types::data_format::DataFormat;
3435
use types::encoded;
3536
use types::filter::Filter;
3637
use types::header::Header;
@@ -477,3 +478,29 @@ pub trait BlockChainReset {
477478
/// reset to best_block - n
478479
fn reset(&self, num: u32) -> Result<(), String>;
479480
}
481+
482+
/// Provides a method for importing/exporting blocks
483+
pub trait ImportExportBlocks {
484+
/// Export blocks to destination, with the given from, to and format argument.
485+
/// destination could be a file or stdout.
486+
/// If the format is hex, each block is written on a new line.
487+
/// For binary exports, all block data is written to the same line.
488+
fn export_blocks<'a>(
489+
&self,
490+
destination: Box<dyn std::io::Write + 'a>,
491+
from: BlockId,
492+
to: BlockId,
493+
format: Option<DataFormat>
494+
) -> Result<(), String>;
495+
496+
/// Import blocks from destination, with the given format argument
497+
/// Source could be a file or stdout.
498+
/// For hex format imports, it attempts to read the blocks on a line by line basis.
499+
/// For binary format imports, reads the 8 byte RLP header in order to decode the block
500+
/// length to be read.
501+
fn import_blocks<'a>(
502+
&self,
503+
source: Box<dyn std::io::Read + 'a>,
504+
format: Option<DataFormat>
505+
) -> Result<(), String>;
506+
}

0 commit comments

Comments
 (0)