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

Commit ea7b318

Browse files
committed
Add new line after writing block to hex file. (#10984)
1 parent 5ee54b7 commit ea7b318

File tree

9 files changed

+324
-129
lines changed

9 files changed

+324
-129
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: 126 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,23 @@
1717
use std::cmp;
1818
use std::collections::{HashSet, BTreeMap, VecDeque};
1919
use std::str::FromStr;
20+
use std::str::from_utf8;
2021
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
2122
use std::sync::{Arc, Weak};
22-
use std::time::{Instant, Duration};
23+
use std::io::{BufReader, BufRead};
24+
use std::time::{Duration, Instant};
2325

2426
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey};
2527
use bytes::Bytes;
26-
use call_contract::{CallContract, RegistryInfo};
27-
use ethcore_miner::pool::VerifiedTransaction;
28-
use ethereum_types::{H256, H264, Address, U256};
29-
use evm::Schedule;
28+
use rlp::PayloadInfo;
29+
use bytes::ToPretty;
30+
use error::Error;
31+
use ethereum_types::{Address, H256, H264, U256};
3032
use hash::keccak;
31-
use io::IoChannel;
33+
use call_contract::CallContract;
34+
use ethcore_miner::pool::VerifiedTransaction;
3235
use itertools::Itertools;
33-
use journaldb;
34-
use kvdb::{DBValue, KeyValueDB, DBTransaction};
36+
use kvdb::{DBTransaction, DBValue, KeyValueDB};
3537
use parking_lot::{Mutex, RwLock};
3638
use rand::OsRng;
3739
use types::transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action};
@@ -43,6 +45,7 @@ use types::log_entry::LocalizedLogEntry;
4345
use types::receipt::{Receipt, LocalizedReceipt};
4446
use types::{BlockNumber, header::{Header, ExtendedHeader}};
4547
use vm::{EnvInfo, LastHashes};
48+
use types::data_format::DataFormat;
4649

4750
use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock};
4851
use client::ancient_import::AncientVerifier;
@@ -51,8 +54,9 @@ use client::{
5154
ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
5255
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
5356
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
54-
ClientIoMessage, BlockChainReset
57+
ClientIoMessage, BlockChainReset, ImportExportBlocks
5558
};
59+
use rustc_hex::FromHex;
5660
use client::{
5761
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
5862
TraceFilter, CallAnalytics, Mode,
@@ -80,7 +84,9 @@ use verification::queue::kind::blocks::Unverified;
8084
use verification::{PreverifiedBlock, Verifier, BlockQueue};
8185
use verification;
8286
use ansi_term::Colour;
83-
87+
use call_contract::RegistryInfo;
88+
use io::IoChannel;
89+
use vm::Schedule;
8490
// re-export
8591
pub use types::blockchain_info::BlockChainInfo;
8692
pub use types::block_status::BlockStatus;
@@ -2568,6 +2574,116 @@ impl Drop for Client {
25682574
}
25692575
}
25702576

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

@@ -477,3 +478,30 @@ 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+
}
507+

ethcore/src/tests/client.rs

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,29 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
1616

17-
use std::str::FromStr;
17+
use std::str::{FromStr, from_utf8};
1818
use std::sync::Arc;
1919

2020
use ethereum_types::{U256, Address};
2121
use ethkey::KeyPair;
2222
use hash::keccak;
2323
use io::IoChannel;
2424
use tempdir::TempDir;
25-
use types::transaction::{PendingTransaction, Transaction, Action, Condition};
26-
use types::filter::Filter;
27-
use types::view;
28-
use types::views::BlockView;
29-
30-
use client::{BlockChainClient, BlockChainReset, Client, ClientConfig, BlockId, ChainInfo, BlockInfo, PrepareOpenBlock, ImportSealedBlock, ImportBlock};
25+
use types::{
26+
data_format::DataFormat,
27+
ids::BlockId,
28+
transaction::{PendingTransaction, Transaction, Action, Condition},
29+
filter::Filter,
30+
view,
31+
views::BlockView,
32+
};
33+
use verification::queue::kind::blocks::Unverified;
34+
use client::{Client, ClientConfig, PrepareOpenBlock, ImportSealedBlock};
35+
use client::traits::{
36+
BlockInfo, BlockChainClient, BlockChainReset, ChainInfo,
37+
ImportExportBlocks, ImportBlock
38+
};
39+
use spec;
3140
use ethereum;
3241
use executive::{Executive, TransactOptions};
3342
use miner::{Miner, PendingOrdering, MinerService};
@@ -38,7 +47,7 @@ use test_helpers::{
3847
generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq,
3948
generate_dummy_client_with_data, get_good_dummy_block, get_bad_state_dummy_block
4049
};
41-
use verification::queue::kind::blocks::Unverified;
50+
use rustc_hex::ToHex;
4251

4352
#[test]
4453
fn imports_from_empty() {
@@ -386,3 +395,79 @@ fn reset_blockchain() {
386395

387396
assert!(client.block_header(BlockId::Number(15)).is_some());
388397
}
398+
399+
#[test]
400+
fn import_export_hex() {
401+
let client = get_test_client_with_blocks(get_good_dummy_block_seq(19));
402+
let block_rlps = (15..20)
403+
.filter_map(|num| client.block(BlockId::Number(num)))
404+
.map(|header| {
405+
header.raw().to_hex()
406+
})
407+
.collect::<Vec<_>>();
408+
409+
let mut out = Vec::new();
410+
411+
client.export_blocks(
412+
Box::new(&mut out),
413+
BlockId::Number(15),
414+
BlockId::Number(20),
415+
Some(DataFormat::Hex)
416+
).unwrap();
417+
418+
let written = from_utf8(&out)
419+
.unwrap()
420+
.split("\n")
421+
// last line is empty, ignore it.
422+
.take(5)
423+
.collect::<Vec<_>>();
424+
assert_eq!(block_rlps, written);
425+
426+
assert!(client.reset(5).is_ok());
427+
client.chain().clear_cache();
428+
429+
assert!(client.block_header(BlockId::Number(20)).is_none());
430+
assert!(client.block_header(BlockId::Number(19)).is_none());
431+
assert!(client.block_header(BlockId::Number(18)).is_none());
432+
assert!(client.block_header(BlockId::Number(17)).is_none());
433+
assert!(client.block_header(BlockId::Number(16)).is_none());
434+
435+
client.import_blocks(Box::new(&*out), Some(DataFormat::Hex)).unwrap();
436+
437+
assert!(client.block_header(BlockId::Number(20)).is_some());
438+
assert!(client.block_header(BlockId::Number(19)).is_some());
439+
assert!(client.block_header(BlockId::Number(18)).is_some());
440+
assert!(client.block_header(BlockId::Number(17)).is_some());
441+
assert!(client.block_header(BlockId::Number(16)).is_some());
442+
}
443+
444+
#[test]
445+
fn import_export_binary() {
446+
let client = get_test_client_with_blocks(get_good_dummy_block_seq(19));
447+
448+
let mut out = Vec::new();
449+
450+
client.export_blocks(
451+
Box::new(&mut out),
452+
BlockId::Number(15),
453+
BlockId::Number(20),
454+
Some(DataFormat::Binary)
455+
).unwrap();
456+
457+
assert!(client.reset(5).is_ok());
458+
client.chain().clear_cache();
459+
460+
assert!(client.block_header(BlockId::Number(20)).is_none());
461+
assert!(client.block_header(BlockId::Number(19)).is_none());
462+
assert!(client.block_header(BlockId::Number(18)).is_none());
463+
assert!(client.block_header(BlockId::Number(17)).is_none());
464+
assert!(client.block_header(BlockId::Number(16)).is_none());
465+
466+
client.import_blocks(Box::new(&*out), Some(DataFormat::Binary)).unwrap();
467+
468+
assert!(client.block_header(BlockId::Number(19)).is_some());
469+
assert!(client.block_header(BlockId::Number(18)).is_some());
470+
assert!(client.block_header(BlockId::Number(20)).is_some());
471+
assert!(client.block_header(BlockId::Number(17)).is_some());
472+
assert!(client.block_header(BlockId::Number(16)).is_some());
473+
}

0 commit comments

Comments
 (0)