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
This commit is contained in:
Seun LanLege
2019-09-09 12:46:05 +01:00
committed by David
parent 53e590f54b
commit 80f0e4b58f
8 changed files with 391 additions and 179 deletions

View File

@@ -15,98 +15,129 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::cmp;
use std::collections::{HashSet, BTreeMap, VecDeque};
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::io::{BufRead, BufReader};
use std::str::from_utf8;
use std::sync::{Arc, Weak};
use std::time::{Instant, Duration};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
use std::time::{Duration, Instant};
use account_state::state::StateInfo;
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, TransactionAddress, ExtrasInsert, BlockNumberKey};
use ansi_term::Colour;
use bytes::Bytes;
use call_contract::{CallContract, RegistryInfo};
use ethcore_miner::pool::VerifiedTransaction;
use ethereum_types::{H256, H264, Address, U256};
use evm::Schedule;
use bytes::ToPretty;
use ethereum_types::{Address, H256, H264, U256};
use hash::keccak;
use io::IoChannel;
use hash_db::EMPTY_PREFIX;
use itertools::Itertools;
use journaldb;
use kvdb::{DBValue, KeyValueDB, DBTransaction};
use kvdb::{DBTransaction, DBValue, KeyValueDB};
use parking_lot::{Mutex, RwLock};
use rand::rngs::OsRng;
use trie::{TrieSpec, TrieFactory, Trie};
use vm::{EnvInfo, LastHashes, CreateContractAddress};
use hash_db::EMPTY_PREFIX;
use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock};
use client::ancient_import::AncientVerifier;
use rlp::PayloadInfo;
use rustc_hex::FromHex;
use trie::{Trie, TrieFactory, TrieSpec};
use account_state::State;
use account_state::state::StateInfo;
use block::{ClosedBlock, Drain, enact_verified, LockedBlock, OpenBlock, SealedBlock};
use blockchain::{
BlockChain,
BlockChainDB,
BlockNumberKey,
BlockProvider,
BlockReceipts,
ExtrasInsert,
TransactionAddress,
TreeRoute
};
// re-export
pub use blockchain::CacheSize as BlockChainCacheSize;
use call_contract::{CallContract, RegistryInfo};
use client::{
ReopenBlock, PrepareOpenBlock, ImportSealedBlock, BroadcastProposalBlock,
Call, BlockProducer, SealedBlockImporter, EngineInfo,
ClientConfig, bad_blocks,
bad_blocks, BlockProducer, BroadcastProposalBlock, Call,
ClientConfig, EngineInfo, ImportSealedBlock, PrepareOpenBlock,
ReopenBlock, SealedBlockImporter,
};
use client::ancient_import::AncientVerifier;
use client_traits::{
BlockInfo, ScheduleInfo, StateClient, BlockChainReset,
Nonce, Balance, ChainInfo, TransactionInfo, ImportBlock,
AccountData, BlockChain as BlockChainTrait, BlockChainClient,
IoClient, BadBlocks, ProvingBlockChainClient, SnapshotClient,
DatabaseRestore, SnapshotWriter, Tick, ChainNotify,
AccountData,
BadBlocks,
Balance,
BlockChain as BlockChainTrait,
BlockChainClient,
BlockChainReset,
BlockInfo,
ChainInfo,
ChainNotify,
DatabaseRestore,
ImportBlock,
ImportExportBlocks,
IoClient,
Nonce,
ProvingBlockChainClient,
ScheduleInfo,
SnapshotClient,
SnapshotWriter,
StateClient,
StateOrBlock,
Tick,
TransactionInfo
};
use db::{keys::BlockDetails, Readable, Writable};
use engine::Engine;
use ethcore_miner::pool::VerifiedTransaction;
use ethtrie::Layout;
use evm::Schedule;
use executive_state;
use io::IoChannel;
use journaldb;
use machine::{
executed::Executed,
executive::{Executive, TransactOptions, contract_address},
executive::{contract_address, Executive, TransactOptions},
transaction_ext::Transaction,
};
use trie_vm_factories::{Factories, VmFactory};
use miner::{Miner, MinerService};
use snapshot;
use spec::Spec;
use account_state::State;
use executive_state;
use state_db::StateDB;
use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace::{self, Database as TraceDatabase, ImportRequest as TraceImportRequest, LocalizedTrace, TraceDB};
use trie_vm_factories::{Factories, VmFactory};
use types::{
ancestry_action::AncestryAction,
BlockNumber,
block::PreverifiedBlock,
block_status::BlockStatus,
blockchain_info::BlockChainInfo,
chain_notify::{NewBlocks, ChainRoute, ChainMessageType},
BlockNumber,
call_analytics::CallAnalytics,
chain_notify::{ChainMessageType, ChainRoute, NewBlocks},
client_types::ClientReport,
import_route::ImportRoute,
io_message::ClientIoMessage,
client_types::Mode,
encoded,
engines::{
ForkChoice,
SealingState,
MAX_UNCLE_AGE,
epoch::{PendingTransition, Transition as EpochTransition},
ForkChoice,
machine::{AuxiliaryData, Call as MachineCall},
MAX_UNCLE_AGE,
SealingState,
},
errors::{EngineError, ExecutionError, BlockError, EthcoreError, SnapshotError, ImportError, EthcoreResult},
ids::{BlockId, TransactionId, UncleId, TraceId},
transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action, CallError},
errors::{BlockError, EngineError, EthcoreError, EthcoreResult, ExecutionError, ImportError, SnapshotError},
filter::Filter,
log_entry::LocalizedLogEntry,
receipt::{Receipt, LocalizedReceipt},
header::Header,
ids::{BlockId, TraceId, TransactionId, UncleId},
import_route::ImportRoute,
io_message::ClientIoMessage,
log_entry::LocalizedLogEntry,
pruning_info::PruningInfo,
receipt::{LocalizedReceipt, Receipt},
snapshot::{Progress, Snapshotting},
trace_filter::Filter as TraceFilter,
pruning_info::PruningInfo,
call_analytics::CallAnalytics,
client_types::Mode,
transaction::{self, Action, CallError, LocalizedTransaction, SignedTransaction, UnverifiedTransaction},
verification::{Unverified, VerificationQueueInfo as BlockQueueInfo},
};
use verification::queue::kind::BlockLike;
use verification::{Verifier, BlockQueue};
use types::data_format::DataFormat;
use verification::{BlockQueue, Verifier};
use verification;
use ansi_term::Colour;
use ethtrie::Layout;
// re-export
pub use blockchain::CacheSize as BlockChainCacheSize;
use db::{Writable, Readable, keys::BlockDetails};
use verification::queue::kind::BlockLike;
use vm::{CreateContractAddress, EnvInfo, LastHashes};
use_contract!(registry, "res/contracts/registrar.json");
@@ -2555,6 +2586,116 @@ impl SnapshotClient for Client {
}
impl ImportExportBlocks for Client {
fn export_blocks<'a>(
&self,
mut out: Box<dyn std::io::Write + 'a>,
from: BlockId,
to: BlockId,
format: Option<DataFormat>
) -> Result<(), String> {
let from = self.block_number(from).ok_or("Starting block could not be found")?;
let to = self.block_number(to).ok_or("End block could not be found")?;
let format = format.unwrap_or_default();
for i in from..=to {
if i % 10000 == 0 {
info!("#{}", i);
}
let b = self.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")?.into_inner();
match format {
DataFormat::Binary => {
out.write(&b).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?;
}
DataFormat::Hex => {
out.write_fmt(format_args!("{}\n", b.pretty())).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?;
}
}
}
Ok(())
}
fn import_blocks<'a>(
&self,
mut source: Box<dyn std::io::Read + 'a>,
format: Option<DataFormat>
) -> Result<(), String> {
const READAHEAD_BYTES: usize = 8;
let mut first_bytes: Vec<u8> = vec![0; READAHEAD_BYTES];
let mut first_read = 0;
let format = match format {
Some(format) => format,
None => {
first_read = source.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>| {
let block = Unverified::from_rlp(bytes).map_err(|_| "Invalid block rlp")?;
let number = block.header.number();
while self.queue_info().is_full() { std::thread::sleep(Duration::from_secs(1)); }
match self.import_block(block) {
Err(EthcoreError::Import(ImportError::AlreadyInChain)) => {
trace!("Skipping block #{}: already in chain.", number);
}
Err(e) => {
return Err(format!("Cannot import block #{}: {:?}", number, e));
},
Ok(_) => {},
}
Ok(())
};
match format {
DataFormat::Binary => {
loop {
let (mut bytes, n) = if first_read > 0 {
(first_bytes.clone(), first_read)
} else {
let mut bytes = vec![0; READAHEAD_BYTES];
let n = source.read(&mut bytes)
.map_err(|err| format!("Error reading from the file/stream: {:?}", err))?;
(bytes, n)
};
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);
source.read_exact(&mut bytes[n..])
.map_err(|err| format!("Error reading from the file/stream: {:?}", err))?;
do_import(bytes)?;
}
}
DataFormat::Hex => {
for line in BufReader::new(source).lines() {
let s = line
.map_err(|err| format!("Error reading from the file/stream: {:?}", err))?;
let s = if first_read > 0 {
from_utf8(&first_bytes)
.map_err(|err| format!("Invalid UTF-8: {:?}", err))?
.to_owned() + &(s[..])
} else {
s
};
first_read = 0;
let bytes = s.from_hex()
.map_err(|err| format!("Invalid hex in file/stream: {:?}", err))?;
do_import(bytes)?;
}
}
};
self.flush_queue();
Ok(())
}
}
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
/// and a vector of receipts from given block up to transaction index.
fn transaction_receipt(
@@ -2634,26 +2775,28 @@ impl IoChannelQueue {
#[cfg(test)]
mod tests {
use ethereum_types::{H256, Address};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;
use ethereum_types::{Address, H256};
use hash::keccak;
use kvdb::DBTransaction;
use blockchain::{ExtrasInsert, BlockProvider};
use client_traits::{BlockChainClient, ChainInfo};
use ethkey::KeyPair;
use types::{
encoded,
engines::ForkChoice,
ids::{BlockId, TransactionId},
log_entry::{LogEntry, LocalizedLogEntry},
receipt::{Receipt, LocalizedReceipt, TransactionOutcome},
transaction::{Transaction, LocalizedTransaction, Action},
log_entry::{LocalizedLogEntry, LogEntry},
receipt::{LocalizedReceipt, Receipt, TransactionOutcome},
transaction::{Action, LocalizedTransaction, Transaction},
};
use test_helpers::{generate_dummy_client, generate_dummy_client_with_data, generate_dummy_client_with_spec_and_data, get_good_dummy_block_hash};
use std::thread;
use std::time::Duration;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use kvdb::DBTransaction;
use blockchain::{BlockProvider, ExtrasInsert};
use hash::keccak;
use super::transaction_receipt;
use ethkey::KeyPair;
#[test]
fn should_not_cache_details_before_commit() {

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::str::FromStr;
use std::str::{FromStr, from_utf8};
use std::sync::Arc;
use ethereum_types::{U256, Address};
@@ -23,6 +23,7 @@ use hash::keccak;
use io::IoChannel;
use tempdir::TempDir;
use types::{
data_format::DataFormat,
ids::BlockId,
transaction::{PendingTransaction, Transaction, Action, Condition},
filter::Filter,
@@ -34,7 +35,7 @@ use types::{
use client::{Client, ClientConfig, PrepareOpenBlock, ImportSealedBlock};
use client_traits::{
BlockInfo, BlockChainClient, BlockChainReset, ChainInfo,
ImportBlock, Tick,
ImportExportBlocks, Tick, ImportBlock
};
use spec;
use machine::executive::{Executive, TransactOptions};
@@ -45,6 +46,7 @@ use test_helpers::{
generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq,
generate_dummy_client_with_data, get_good_dummy_block, get_bad_state_dummy_block
};
use rustc_hex::ToHex;
#[test]
fn imports_from_empty() {
@@ -387,3 +389,79 @@ fn reset_blockchain() {
assert!(client.block_header(BlockId::Number(15)).is_some());
}
#[test]
fn import_export_hex() {
let client = get_test_client_with_blocks(get_good_dummy_block_seq(19));
let block_rlps = (15..20)
.filter_map(|num| client.block(BlockId::Number(num)))
.map(|header| {
header.raw().to_hex()
})
.collect::<Vec<_>>();
let mut out = Vec::new();
client.export_blocks(
Box::new(&mut out),
BlockId::Number(15),
BlockId::Number(20),
Some(DataFormat::Hex)
).unwrap();
let written = from_utf8(&out)
.unwrap()
.split("\n")
// last line is empty, ignore it.
.take(5)
.collect::<Vec<_>>();
assert_eq!(block_rlps, written);
assert!(client.reset(5).is_ok());
client.chain().clear_cache();
assert!(client.block_header(BlockId::Number(20)).is_none());
assert!(client.block_header(BlockId::Number(19)).is_none());
assert!(client.block_header(BlockId::Number(18)).is_none());
assert!(client.block_header(BlockId::Number(17)).is_none());
assert!(client.block_header(BlockId::Number(16)).is_none());
client.import_blocks(Box::new(&*out), Some(DataFormat::Hex)).unwrap();
assert!(client.block_header(BlockId::Number(20)).is_some());
assert!(client.block_header(BlockId::Number(19)).is_some());
assert!(client.block_header(BlockId::Number(18)).is_some());
assert!(client.block_header(BlockId::Number(17)).is_some());
assert!(client.block_header(BlockId::Number(16)).is_some());
}
#[test]
fn import_export_binary() {
let client = get_test_client_with_blocks(get_good_dummy_block_seq(19));
let mut out = Vec::new();
client.export_blocks(
Box::new(&mut out),
BlockId::Number(15),
BlockId::Number(20),
Some(DataFormat::Binary)
).unwrap();
assert!(client.reset(5).is_ok());
client.chain().clear_cache();
assert!(client.block_header(BlockId::Number(20)).is_none());
assert!(client.block_header(BlockId::Number(19)).is_none());
assert!(client.block_header(BlockId::Number(18)).is_none());
assert!(client.block_header(BlockId::Number(17)).is_none());
assert!(client.block_header(BlockId::Number(16)).is_none());
client.import_blocks(Box::new(&*out), Some(DataFormat::Binary)).unwrap();
assert!(client.block_header(BlockId::Number(19)).is_some());
assert!(client.block_header(BlockId::Number(18)).is_some());
assert!(client.block_header(BlockId::Number(20)).is_some());
assert!(client.block_header(BlockId::Number(17)).is_some());
assert!(client.block_header(BlockId::Number(16)).is_some());
}