[beta] Backports (#8558)
* Fetching logs by hash in blockchain database (#8463) * Fetch logs by hash in blockchain database * Fix tests * Add unit test for branch block logs fetching * Add docs that blocks must already be sorted * Handle branch block cases properly * typo: empty -> is_empty * Remove return_empty_if_none by using a closure * Use BTreeSet to avoid sorting again * Move is_canon to BlockChain * typo: pass value by reference * Use loop and wrap inside blocks to simplify the code Borrowed from https://github.com/paritytech/parity/pull/8463#discussion_r183453326 * typo: missed a comment * Pass on storage keys tracing to handle the case when it is not modified (#8491) * Pass on storage keys even if it is not modified * typo: account and storage query `to_pod_diff` builds both `touched_addresses` merge and storage keys merge. * Fix tests * Use state query directly because of suicided accounts * Fix a RefCell borrow issue * Add tests for unmodified storage trace * Address grumbles * typo: remove unwanted empty line * ensure_cached compiles with the original signature * Update wasmi and pwasm-utils (#8493) * Update wasmi to 0.2 New wasmi supports 32bit platforms and no longer requires a special feature to build for such platforms. * Update pwasm-utils to 0.1.5 * Show imported messages for light client (#8517) * Enable WebAssembly and Byzantium for Ellaism (#8520) * Enable WebAssembly and Byzantium for Ellaism * Fix indentation * Remove empty lines * Don't panic in import_block if invalid rlp (#8522) * Don't panic in import_block if invalid rlp * Remove redundant type annotation * Replace RLP header view usage with safe decoding Using the view will panic with invalid RLP. Here we use Rlp decoding directly which will return a `Result<_, DecoderError>`. While this path currently should not have any invalid RLP - it makes it safer if ever called with invalid RLP from other code paths. * Node table sorting according to last contact data (#8541) * network-devp2p: sort nodes in node table using last contact data * network-devp2p: rename node contact types in node table json output * network-devp2p: fix node table tests * network-devp2p: note node failure when failed to establish connection * network-devp2p: handle UselessPeer error * network-devp2p: note failure when marking node as useless
This commit is contained in:
@@ -57,6 +57,12 @@ pub trait BlockProvider {
|
||||
/// (though not necessarily a part of the canon chain).
|
||||
fn is_known(&self, hash: &H256) -> bool;
|
||||
|
||||
/// Returns true if the given block is known and in the canon chain.
|
||||
fn is_canon(&self, hash: &H256) -> bool {
|
||||
let is_canon = || Some(hash == &self.block_hash(self.block_number(hash)?)?);
|
||||
is_canon().unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Get the first block of the best part of the chain.
|
||||
/// Return `None` if there is no gap and the first block is the genesis.
|
||||
/// Any queries of blocks which precede this one are not guaranteed to
|
||||
@@ -148,7 +154,7 @@ pub trait BlockProvider {
|
||||
fn blocks_with_bloom(&self, bloom: &Bloom, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;
|
||||
|
||||
/// Returns logs matching given filter.
|
||||
fn logs<F>(&self, blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
fn logs<F>(&self, blocks: Vec<H256>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized;
|
||||
}
|
||||
|
||||
@@ -332,16 +338,18 @@ impl BlockProvider for BlockChain {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn logs<F>(&self, mut blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
/// Returns logs matching given filter. The order of logs returned will be the same as the order of the blocks
|
||||
/// provided. And it's the callers responsibility to sort blocks provided in advance.
|
||||
fn logs<F>(&self, mut blocks: Vec<H256>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized {
|
||||
// sort in reverse order
|
||||
blocks.sort_by(|a, b| b.cmp(a));
|
||||
blocks.reverse();
|
||||
|
||||
let mut logs = blocks
|
||||
.chunks(128)
|
||||
.flat_map(move |blocks_chunk| {
|
||||
blocks_chunk.into_par_iter()
|
||||
.filter_map(|number| self.block_hash(*number).map(|hash| (*number, hash)))
|
||||
.filter_map(|hash| self.block_number(&hash).map(|r| (r, hash)))
|
||||
.filter_map(|(number, hash)| self.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
||||
.filter_map(|(number, hash, receipts)| self.block_body(&hash).map(|ref b| (number, hash, receipts, b.transaction_hashes())))
|
||||
.flat_map(|(number, hash, mut receipts, mut hashes)| {
|
||||
@@ -368,7 +376,7 @@ impl BlockProvider for BlockChain {
|
||||
.enumerate()
|
||||
.map(move |(i, log)| LocalizedLogEntry {
|
||||
entry: log,
|
||||
block_hash: hash,
|
||||
block_hash: *hash,
|
||||
block_number: number,
|
||||
transaction_hash: tx_hash,
|
||||
// iterating in reverse order
|
||||
@@ -1933,17 +1941,33 @@ mod tests {
|
||||
value: 103.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&secret(), None);
|
||||
let t4 = Transaction {
|
||||
nonce: 0.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 100_000.into(),
|
||||
action: Action::Create,
|
||||
value: 104.into(),
|
||||
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
|
||||
}.sign(&secret(), None);
|
||||
let tx_hash1 = t1.hash();
|
||||
let tx_hash2 = t2.hash();
|
||||
let tx_hash3 = t3.hash();
|
||||
let tx_hash4 = t4.hash();
|
||||
|
||||
let genesis = BlockBuilder::genesis();
|
||||
let b1 = genesis.add_block_with_transactions(vec![t1, t2]);
|
||||
let b2 = b1.add_block_with_transactions(iter::once(t3));
|
||||
let b3 = genesis.add_block_with(|| BlockOptions {
|
||||
transactions: vec![t4.clone()],
|
||||
difficulty: U256::from(9),
|
||||
..Default::default()
|
||||
}); // Branch block
|
||||
let b1_hash = b1.last().hash();
|
||||
let b1_number = b1.last().number();
|
||||
let b2_hash = b2.last().hash();
|
||||
let b2_number = b2.last().number();
|
||||
let b3_hash = b3.last().hash();
|
||||
let b3_number = b3.last().number();
|
||||
|
||||
let db = new_db();
|
||||
let bc = new_chain(&genesis.last().encoded(), db.clone());
|
||||
@@ -1974,10 +1998,21 @@ mod tests {
|
||||
],
|
||||
}
|
||||
]);
|
||||
insert_block(&db, &bc, &b3.last().encoded(), vec![
|
||||
Receipt {
|
||||
outcome: TransactionOutcome::StateRoot(H256::default()),
|
||||
gas_used: 10_000.into(),
|
||||
log_bloom: Default::default(),
|
||||
logs: vec![
|
||||
LogEntry { address: Default::default(), topics: vec![], data: vec![5], },
|
||||
],
|
||||
}
|
||||
]);
|
||||
|
||||
// when
|
||||
let logs1 = bc.logs(vec![1, 2], |_| true, None);
|
||||
let logs2 = bc.logs(vec![1, 2], |_| true, Some(1));
|
||||
let logs1 = bc.logs(vec![b1_hash, b2_hash], |_| true, None);
|
||||
let logs2 = bc.logs(vec![b1_hash, b2_hash], |_| true, Some(1));
|
||||
let logs3 = bc.logs(vec![b3_hash], |_| true, None);
|
||||
|
||||
// then
|
||||
assert_eq!(logs1, vec![
|
||||
@@ -2029,6 +2064,17 @@ mod tests {
|
||||
log_index: 0,
|
||||
}
|
||||
]);
|
||||
assert_eq!(logs3, vec![
|
||||
LocalizedLogEntry {
|
||||
entry: LogEntry { address: Default::default(), topics: vec![], data: vec![5] },
|
||||
block_hash: b3_hash,
|
||||
block_number: b3_number,
|
||||
transaction_hash: tx_hash4,
|
||||
transaction_index: 0,
|
||||
transaction_log_index: 0,
|
||||
log_index: 0,
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{HashSet, HashMap, BTreeMap, VecDeque};
|
||||
use std::collections::{HashSet, HashMap, BTreeMap, BTreeSet, VecDeque};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
@@ -452,9 +452,7 @@ impl Importer {
|
||||
///
|
||||
/// The block is guaranteed to be the next best blocks in the
|
||||
/// first block sequence. Does no sealing or transaction validation.
|
||||
fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes, db: &KeyValueDB, chain: &BlockChain) -> Result<H256, ::error::Error> {
|
||||
let block = view!(BlockView, &block_bytes);
|
||||
let header = block.header();
|
||||
fn import_old_block(&self, header: &Header, block_bytes: Bytes, receipts_bytes: Bytes, db: &KeyValueDB, chain: &BlockChain) -> Result<H256, ::error::Error> {
|
||||
let receipts = ::rlp::decode_list(&receipts_bytes);
|
||||
let hash = header.hash();
|
||||
let _import_lock = self.import_lock.lock();
|
||||
@@ -1413,7 +1411,7 @@ impl ImportBlock for Client {
|
||||
use verification::queue::kind::blocks::Unverified;
|
||||
|
||||
// create unverified block here so the `keccak` calculation can be cached.
|
||||
let unverified = Unverified::new(bytes);
|
||||
let unverified = Unverified::from_rlp(bytes)?;
|
||||
|
||||
{
|
||||
if self.chain.read().is_known(&unverified.hash()) {
|
||||
@@ -1428,19 +1426,19 @@ impl ImportBlock for Client {
|
||||
}
|
||||
|
||||
fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError> {
|
||||
let header: Header = ::rlp::Rlp::new(&block_bytes).val_at(0)?;
|
||||
{
|
||||
// check block order
|
||||
let header = view!(BlockView, &block_bytes).header_view();
|
||||
if self.chain.read().is_known(&header.hash()) {
|
||||
bail!(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain));
|
||||
}
|
||||
let status = self.block_status(BlockId::Hash(header.parent_hash()));
|
||||
if status == BlockStatus::Unknown || status == BlockStatus::Pending {
|
||||
bail!(BlockImportErrorKind::Block(BlockError::UnknownParent(header.parent_hash())));
|
||||
let status = self.block_status(BlockId::Hash(*header.parent_hash()));
|
||||
if status == BlockStatus::Unknown || status == BlockStatus::Pending {
|
||||
bail!(BlockImportErrorKind::Block(BlockError::UnknownParent(*header.parent_hash())));
|
||||
}
|
||||
}
|
||||
|
||||
self.importer.import_old_block(block_bytes, receipts_bytes, &**self.db.read(), &*self.chain.read()).map_err(Into::into)
|
||||
self.importer.import_old_block(&header, block_bytes, receipts_bytes, &**self.db.read(), &*self.chain.read()).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1853,23 +1851,82 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> {
|
||||
let (from, to) = match (self.block_number_ref(&filter.from_block), self.block_number_ref(&filter.to_block)) {
|
||||
(Some(from), Some(to)) => (from, to),
|
||||
_ => return Vec::new(),
|
||||
// Wrap the logic inside a closure so that we can take advantage of question mark syntax.
|
||||
let fetch_logs = || {
|
||||
let chain = self.chain.read();
|
||||
|
||||
// First, check whether `filter.from_block` and `filter.to_block` is on the canon chain. If so, we can use the
|
||||
// optimized version.
|
||||
let is_canon = |id| {
|
||||
match id {
|
||||
// If it is referred by number, then it is always on the canon chain.
|
||||
&BlockId::Earliest | &BlockId::Latest | &BlockId::Number(_) => true,
|
||||
// If it is referred by hash, we see whether a hash -> number -> hash conversion gives us the same
|
||||
// result.
|
||||
&BlockId::Hash(ref hash) => chain.is_canon(hash),
|
||||
}
|
||||
};
|
||||
|
||||
let blocks = if is_canon(&filter.from_block) && is_canon(&filter.to_block) {
|
||||
// If we are on the canon chain, use bloom filter to fetch required hashes.
|
||||
let from = self.block_number_ref(&filter.from_block)?;
|
||||
let to = self.block_number_ref(&filter.to_block)?;
|
||||
|
||||
filter.bloom_possibilities().iter()
|
||||
.map(|bloom| {
|
||||
chain.blocks_with_bloom(bloom, from, to)
|
||||
})
|
||||
.flat_map(|m| m)
|
||||
// remove duplicate elements
|
||||
.collect::<BTreeSet<u64>>()
|
||||
.into_iter()
|
||||
.filter_map(|n| chain.block_hash(n))
|
||||
.collect::<Vec<H256>>()
|
||||
|
||||
} else {
|
||||
// Otherwise, we use a slower version that finds a link between from_block and to_block.
|
||||
let from_hash = Self::block_hash(&chain, filter.from_block)?;
|
||||
let from_number = chain.block_number(&from_hash)?;
|
||||
let to_hash = Self::block_hash(&chain, filter.from_block)?;
|
||||
|
||||
let blooms = filter.bloom_possibilities();
|
||||
let bloom_match = |header: &encoded::Header| {
|
||||
blooms.iter().any(|bloom| header.log_bloom().contains_bloom(bloom))
|
||||
};
|
||||
|
||||
let (blocks, last_hash) = {
|
||||
let mut blocks = Vec::new();
|
||||
let mut current_hash = to_hash;
|
||||
|
||||
loop {
|
||||
let header = chain.block_header_data(¤t_hash)?;
|
||||
if bloom_match(&header) {
|
||||
blocks.push(current_hash);
|
||||
}
|
||||
|
||||
// Stop if `from` block is reached.
|
||||
if header.number() <= from_number {
|
||||
break;
|
||||
}
|
||||
current_hash = header.parent_hash();
|
||||
}
|
||||
|
||||
blocks.reverse();
|
||||
(blocks, current_hash)
|
||||
};
|
||||
|
||||
// Check if we've actually reached the expected `from` block.
|
||||
if last_hash != from_hash || blocks.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
blocks
|
||||
};
|
||||
|
||||
Some(self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit))
|
||||
};
|
||||
|
||||
let chain = self.chain.read();
|
||||
let blocks = filter.bloom_possibilities().iter()
|
||||
.map(move |bloom| {
|
||||
chain.blocks_with_bloom(bloom, from, to)
|
||||
})
|
||||
.flat_map(|m| m)
|
||||
// remove duplicate elements
|
||||
.collect::<HashSet<u64>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<u64>>();
|
||||
|
||||
self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)
|
||||
fetch_logs().unwrap_or_default()
|
||||
}
|
||||
|
||||
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>> {
|
||||
|
||||
@@ -190,6 +190,7 @@ error_chain! {
|
||||
|
||||
foreign_links {
|
||||
Block(BlockError) #[doc = "Block error"];
|
||||
Decoder(::rlp::DecoderError) #[doc = "Rlp decoding error"];
|
||||
}
|
||||
|
||||
errors {
|
||||
@@ -206,6 +207,7 @@ impl From<Error> for BlockImportError {
|
||||
match e {
|
||||
Error(ErrorKind::Block(block_error), _) => BlockImportErrorKind::Block(block_error).into(),
|
||||
Error(ErrorKind::Import(import_error), _) => BlockImportErrorKind::Import(import_error.into()).into(),
|
||||
Error(ErrorKind::Util(util_error::ErrorKind::Decoder(decoder_err)), _) => BlockImportErrorKind::Decoder(decoder_err).into(),
|
||||
_ => BlockImportErrorKind::Other(format!("other block import error: {:?}", e)).into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, BTreeMap, HashSet};
|
||||
use std::collections::{HashMap, BTreeMap, BTreeSet, HashSet};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY};
|
||||
@@ -862,40 +862,65 @@ impl<B: Backend> State<B> {
|
||||
}))
|
||||
}
|
||||
|
||||
// Return a list of all touched addresses in cache.
|
||||
fn touched_addresses(&self) -> Vec<Address> {
|
||||
/// Populate a PodAccount map from this state, with another state as the account and storage query.
|
||||
pub fn to_pod_diff<X: Backend>(&mut self, query: &State<X>) -> trie::Result<PodState> {
|
||||
assert!(self.checkpoints.borrow().is_empty());
|
||||
self.cache.borrow().iter().map(|(add, _)| *add).collect()
|
||||
}
|
||||
|
||||
fn query_pod(&mut self, query: &PodState, touched_addresses: &[Address]) -> trie::Result<()> {
|
||||
let pod = query.get();
|
||||
// Merge PodAccount::to_pod for cache of self and `query`.
|
||||
let all_addresses = self.cache.borrow().keys().cloned()
|
||||
.chain(query.cache.borrow().keys().cloned())
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
for address in touched_addresses {
|
||||
if !self.ensure_cached(address, RequireCache::Code, true, |a| a.is_some())? {
|
||||
continue
|
||||
Ok(PodState::from(all_addresses.into_iter().fold(Ok(BTreeMap::new()), |m: trie::Result<_>, address| {
|
||||
let mut m = m?;
|
||||
|
||||
let account = self.ensure_cached(&address, RequireCache::Code, true, |acc| {
|
||||
acc.map(|acc| {
|
||||
// Merge all modified storage keys.
|
||||
let all_keys = {
|
||||
let self_keys = acc.storage_changes().keys().cloned()
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
if let Some(ref query_storage) = query.cache.borrow().get(&address)
|
||||
.and_then(|opt| {
|
||||
Some(opt.account.as_ref()?.storage_changes().keys().cloned()
|
||||
.collect::<BTreeSet<_>>())
|
||||
})
|
||||
{
|
||||
self_keys.union(&query_storage).cloned().collect::<Vec<_>>()
|
||||
} else {
|
||||
self_keys.into_iter().collect::<Vec<_>>()
|
||||
}
|
||||
};
|
||||
|
||||
// Storage must be fetched after ensure_cached to avoid borrow problem.
|
||||
(*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()))
|
||||
})
|
||||
})?;
|
||||
|
||||
if let Some((balance, nonce, storage_keys, code)) = account {
|
||||
let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: trie::Result<_>, key| {
|
||||
let mut s = s?;
|
||||
|
||||
s.insert(key, self.storage_at(&address, &key)?);
|
||||
Ok(s)
|
||||
})?;
|
||||
|
||||
m.insert(address, PodAccount {
|
||||
balance, nonce, storage, code
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(pod_account) = pod.get(address) {
|
||||
// needs to be split into two parts for the refcell code here
|
||||
// to work.
|
||||
for key in pod_account.storage.keys() {
|
||||
self.storage_at(address, key)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(m)
|
||||
})?))
|
||||
}
|
||||
|
||||
/// Returns a `StateDiff` describing the difference from `orig` to `self`.
|
||||
/// Consumes self.
|
||||
pub fn diff_from<X: Backend>(&self, orig: State<X>) -> trie::Result<StateDiff> {
|
||||
let addresses_post = self.touched_addresses();
|
||||
pub fn diff_from<X: Backend>(&self, mut orig: State<X>) -> trie::Result<StateDiff> {
|
||||
let pod_state_post = self.to_pod();
|
||||
let mut state_pre = orig;
|
||||
state_pre.query_pod(&pod_state_post, &addresses_post)?;
|
||||
Ok(pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post))
|
||||
let pod_state_pre = orig.to_pod_diff(self)?;
|
||||
Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post))
|
||||
}
|
||||
|
||||
// load required account data from the databases.
|
||||
@@ -2243,9 +2268,6 @@ mod tests {
|
||||
let original = state.clone();
|
||||
state.kill_account(&a);
|
||||
|
||||
assert_eq!(original.touched_addresses(), vec![]);
|
||||
assert_eq!(state.touched_addresses(), vec![a]);
|
||||
|
||||
let diff = state.diff_from(original).unwrap();
|
||||
let diff_map = diff.get();
|
||||
assert_eq!(diff_map.len(), 1);
|
||||
@@ -2258,4 +2280,42 @@ mod tests {
|
||||
storage: Default::default()
|
||||
}), None).as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_trace_diff_unmodified_storage() {
|
||||
use pod_account;
|
||||
|
||||
let a = 10.into();
|
||||
let db = get_temp_state_db();
|
||||
|
||||
let (root, db) = {
|
||||
let mut state = State::new(db, U256::from(0), Default::default());
|
||||
state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64))).unwrap();
|
||||
state.commit().unwrap();
|
||||
state.drop()
|
||||
};
|
||||
|
||||
let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap();
|
||||
let original = state.clone();
|
||||
state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64))).unwrap();
|
||||
|
||||
let diff = state.diff_from(original).unwrap();
|
||||
let diff_map = diff.get();
|
||||
assert_eq!(diff_map.len(), 1);
|
||||
assert!(diff_map.get(&a).is_some());
|
||||
assert_eq!(diff_map.get(&a),
|
||||
pod_account::diff_pod(Some(&PodAccount {
|
||||
balance: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
code: Some(Default::default()),
|
||||
storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(20u64)))]
|
||||
.into_iter().collect(),
|
||||
}), Some(&PodAccount {
|
||||
balance: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
code: Some(Default::default()),
|
||||
storage: vec![(H256::from(&U256::from(1u64)), H256::from(&U256::from(100u64)))]
|
||||
.into_iter().collect(),
|
||||
})).as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ use views::BlockView;
|
||||
use ethkey::KeyPair;
|
||||
use transaction::{PendingTransaction, Transaction, Action, Condition};
|
||||
use miner::MinerService;
|
||||
use rlp::{RlpStream, EMPTY_LIST_RLP};
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
@@ -111,6 +112,25 @@ fn imports_good_block() {
|
||||
assert!(!block.into_inner().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_to_import_block_with_invalid_rlp() {
|
||||
use error::{BlockImportError, BlockImportErrorKind};
|
||||
|
||||
let client = generate_dummy_client(6);
|
||||
let mut rlp = RlpStream::new_list(3);
|
||||
rlp.append_raw(&EMPTY_LIST_RLP, 1); // empty header
|
||||
rlp.append_raw(&EMPTY_LIST_RLP, 1);
|
||||
rlp.append_raw(&EMPTY_LIST_RLP, 1);
|
||||
let invalid_header_block = rlp.out();
|
||||
|
||||
match client.import_block(invalid_header_block) {
|
||||
Err(BlockImportError(BlockImportErrorKind::Decoder(_), _)) => (), // all good
|
||||
Err(_) => panic!("Should fail with a decoder error"),
|
||||
Ok(_) => panic!("Should not import block with invalid header"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn query_none_block() {
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
|
||||
@@ -119,14 +119,13 @@ pub mod blocks {
|
||||
|
||||
impl Unverified {
|
||||
/// Create an `Unverified` from raw bytes.
|
||||
pub fn new(bytes: Bytes) -> Self {
|
||||
use views::BlockView;
|
||||
pub fn from_rlp(bytes: Bytes) -> Result<Self, ::rlp::DecoderError> {
|
||||
|
||||
let header = view!(BlockView, &bytes).header();
|
||||
Unverified {
|
||||
let header = ::rlp::Rlp::new(&bytes).val_at(0)?;
|
||||
Ok(Unverified {
|
||||
header: header,
|
||||
bytes: bytes,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -734,6 +734,7 @@ mod tests {
|
||||
use test_helpers::{get_good_dummy_block_seq, get_good_dummy_block};
|
||||
use error::*;
|
||||
use views::BlockView;
|
||||
use bytes::Bytes;
|
||||
|
||||
// create a test block queue.
|
||||
// auto_scaling enables verifier adjustment.
|
||||
@@ -746,6 +747,10 @@ mod tests {
|
||||
BlockQueue::new(config, engine, IoChannel::disconnected(), true)
|
||||
}
|
||||
|
||||
fn new_unverified(bytes: Bytes) -> Unverified {
|
||||
Unverified::from_rlp(bytes).expect("Should be valid rlp")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_created() {
|
||||
// TODO better test
|
||||
@@ -757,7 +762,7 @@ mod tests {
|
||||
#[test]
|
||||
fn can_import_blocks() {
|
||||
let queue = get_test_queue(false);
|
||||
if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) {
|
||||
if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) {
|
||||
panic!("error importing block that is valid by definition({:?})", e);
|
||||
}
|
||||
}
|
||||
@@ -765,11 +770,11 @@ mod tests {
|
||||
#[test]
|
||||
fn returns_error_for_duplicates() {
|
||||
let queue = get_test_queue(false);
|
||||
if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) {
|
||||
if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) {
|
||||
panic!("error importing block that is valid by definition({:?})", e);
|
||||
}
|
||||
|
||||
let duplicate_import = queue.import(Unverified::new(get_good_dummy_block()));
|
||||
let duplicate_import = queue.import(new_unverified(get_good_dummy_block()));
|
||||
match duplicate_import {
|
||||
Err(e) => {
|
||||
match e {
|
||||
@@ -786,7 +791,7 @@ mod tests {
|
||||
let queue = get_test_queue(false);
|
||||
let block = get_good_dummy_block();
|
||||
let hash = view!(BlockView, &block).header().hash().clone();
|
||||
if let Err(e) = queue.import(Unverified::new(block)) {
|
||||
if let Err(e) = queue.import(new_unverified(block)) {
|
||||
panic!("error importing block that is valid by definition({:?})", e);
|
||||
}
|
||||
queue.flush();
|
||||
@@ -802,14 +807,14 @@ mod tests {
|
||||
let queue = get_test_queue(false);
|
||||
let block = get_good_dummy_block();
|
||||
let hash = view!(BlockView, &block).header().hash().clone();
|
||||
if let Err(e) = queue.import(Unverified::new(block)) {
|
||||
if let Err(e) = queue.import(new_unverified(block)) {
|
||||
panic!("error importing block that is valid by definition({:?})", e);
|
||||
}
|
||||
queue.flush();
|
||||
queue.drain(10);
|
||||
queue.mark_as_good(&[ hash ]);
|
||||
|
||||
if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) {
|
||||
if let Err(e) = queue.import(new_unverified(get_good_dummy_block())) {
|
||||
panic!("error importing block that has already been drained ({:?})", e);
|
||||
}
|
||||
}
|
||||
@@ -817,7 +822,7 @@ mod tests {
|
||||
#[test]
|
||||
fn returns_empty_once_finished() {
|
||||
let queue = get_test_queue(false);
|
||||
queue.import(Unverified::new(get_good_dummy_block()))
|
||||
queue.import(new_unverified(get_good_dummy_block()))
|
||||
.expect("error importing block that is valid by definition");
|
||||
queue.flush();
|
||||
queue.drain(1);
|
||||
@@ -835,7 +840,7 @@ mod tests {
|
||||
assert!(!queue.queue_info().is_full());
|
||||
let mut blocks = get_good_dummy_block_seq(50);
|
||||
for b in blocks.drain(..) {
|
||||
queue.import(Unverified::new(b)).unwrap();
|
||||
queue.import(new_unverified(b)).unwrap();
|
||||
}
|
||||
assert!(queue.queue_info().is_full());
|
||||
}
|
||||
@@ -863,7 +868,7 @@ mod tests {
|
||||
*queue.state.0.lock() = State::Work(0);
|
||||
|
||||
for block in get_good_dummy_block_seq(5000) {
|
||||
queue.import(Unverified::new(block)).expect("Block good by definition; qed");
|
||||
queue.import(new_unverified(block)).expect("Block good by definition; qed");
|
||||
}
|
||||
|
||||
// almost all unverified == bump verifier count.
|
||||
|
||||
@@ -476,7 +476,7 @@ mod tests {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn logs<F>(&self, _blocks: Vec<BlockNumber>, _matches: F, _limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
fn logs<F>(&self, _blocks: Vec<H256>, _matches: F, _limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||
where F: Fn(&LogEntry) -> bool, Self: Sized {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user