Merge branch 'master' into rpc_poll_ids
This commit is contained in:
@@ -92,10 +92,10 @@ impl Account {
|
||||
|
||||
/// Create a new contract account.
|
||||
/// NOTE: make sure you use `init_code` on this before `commit`ing.
|
||||
pub fn new_contract(balance: U256) -> Account {
|
||||
pub fn new_contract(balance: U256, nonce: U256) -> Account {
|
||||
Account {
|
||||
balance: balance,
|
||||
nonce: U256::from(0u8),
|
||||
nonce: nonce,
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
storage_overlay: RefCell::new(HashMap::new()),
|
||||
code_hash: None,
|
||||
@@ -261,7 +261,7 @@ mod tests {
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
let rlp = {
|
||||
let mut a = Account::new_contract(U256::from(69u8));
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
|
||||
a.commit_storage(&mut db);
|
||||
a.init_code(vec![]);
|
||||
@@ -281,7 +281,7 @@ mod tests {
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
|
||||
let rlp = {
|
||||
let mut a = Account::new_contract(U256::from(69u8));
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
||||
a.commit_code(&mut db);
|
||||
a.rlp()
|
||||
@@ -296,7 +296,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn commit_storage() {
|
||||
let mut a = Account::new_contract(U256::from(69u8));
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(x!(0), x!(0x1234));
|
||||
@@ -307,7 +307,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn commit_remove_commit_storage() {
|
||||
let mut a = Account::new_contract(U256::from(69u8));
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(x!(0), x!(0x1234));
|
||||
@@ -321,7 +321,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn commit_code() {
|
||||
let mut a = Account::new_contract(U256::from(69u8));
|
||||
let mut a = Account::new_contract(x!(69), x!(0));
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Evm input params.
|
||||
use util::hash::*;
|
||||
use util::uint::*;
|
||||
use util::bytes::*;
|
||||
use common::*;
|
||||
|
||||
/// Transaction value
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -28,6 +28,31 @@ use service::*;
|
||||
use client::BlockStatus;
|
||||
use util::panics::*;
|
||||
|
||||
known_heap_size!(0, UnVerifiedBlock, VerifyingBlock, PreVerifiedBlock);
|
||||
|
||||
const MIN_MEM_LIMIT: usize = 16384;
|
||||
const MIN_QUEUE_LIMIT: usize = 512;
|
||||
|
||||
/// Block queue configuration
|
||||
#[derive(Debug)]
|
||||
pub struct BlockQueueConfig {
|
||||
/// Maximum number of blocks to keep in unverified queue.
|
||||
/// When the limit is reached, is_full returns true.
|
||||
pub max_queue_size: usize,
|
||||
/// Maximum heap memory to use.
|
||||
/// When the limit is reached, is_full returns true.
|
||||
pub max_mem_use: usize,
|
||||
}
|
||||
|
||||
impl Default for BlockQueueConfig {
|
||||
fn default() -> Self {
|
||||
BlockQueueConfig {
|
||||
max_queue_size: 30000,
|
||||
max_mem_use: 50 * 1024 * 1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block queue status
|
||||
#[derive(Debug)]
|
||||
pub struct BlockQueueInfo {
|
||||
@@ -37,6 +62,12 @@ pub struct BlockQueueInfo {
|
||||
pub verified_queue_size: usize,
|
||||
/// Number of blocks being verified
|
||||
pub verifying_queue_size: usize,
|
||||
/// Configured maximum number of blocks in the queue
|
||||
pub max_queue_size: usize,
|
||||
/// Configured maximum number of bytes to use
|
||||
pub max_mem_use: usize,
|
||||
/// Heap memory used in bytes
|
||||
pub mem_used: usize,
|
||||
}
|
||||
|
||||
impl BlockQueueInfo {
|
||||
@@ -48,7 +79,8 @@ impl BlockQueueInfo {
|
||||
|
||||
/// Indicates that queue is full
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size > MAX_UNVERIFIED_QUEUE_SIZE
|
||||
self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size > self.max_queue_size ||
|
||||
self.mem_used > self.max_mem_use
|
||||
}
|
||||
|
||||
/// Indicates that queue is empty
|
||||
@@ -68,7 +100,9 @@ pub struct BlockQueue {
|
||||
deleting: Arc<AtomicBool>,
|
||||
ready_signal: Arc<QueueSignal>,
|
||||
empty: Arc<Condvar>,
|
||||
processing: RwLock<HashSet<H256>>
|
||||
processing: RwLock<HashSet<H256>>,
|
||||
max_queue_size: usize,
|
||||
max_mem_use: usize,
|
||||
}
|
||||
|
||||
struct UnVerifiedBlock {
|
||||
@@ -106,11 +140,9 @@ struct Verification {
|
||||
bad: HashSet<H256>,
|
||||
}
|
||||
|
||||
const MAX_UNVERIFIED_QUEUE_SIZE: usize = 50000;
|
||||
|
||||
impl BlockQueue {
|
||||
/// Creates a new queue instance.
|
||||
pub fn new(engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
|
||||
pub fn new(config: BlockQueueConfig, engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
|
||||
let verification = Arc::new(Mutex::new(Verification::default()));
|
||||
let more_to_verify = Arc::new(Condvar::new());
|
||||
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
|
||||
@@ -133,7 +165,7 @@ impl BlockQueue {
|
||||
.name(format!("Verifier #{}", i))
|
||||
.spawn(move || {
|
||||
panic_handler.catch_panic(move || {
|
||||
BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty)
|
||||
BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty)
|
||||
}).unwrap()
|
||||
})
|
||||
.expect("Error starting block verification thread")
|
||||
@@ -149,6 +181,8 @@ impl BlockQueue {
|
||||
deleting: deleting.clone(),
|
||||
processing: RwLock::new(HashSet::new()),
|
||||
empty: empty.clone(),
|
||||
max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT),
|
||||
max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,8 +374,26 @@ impl BlockQueue {
|
||||
verified_queue_size: verification.verified.len(),
|
||||
unverified_queue_size: verification.unverified.len(),
|
||||
verifying_queue_size: verification.verifying.len(),
|
||||
max_queue_size: self.max_queue_size,
|
||||
max_mem_use: self.max_mem_use,
|
||||
mem_used:
|
||||
verification.unverified.heap_size_of_children()
|
||||
+ verification.verifying.heap_size_of_children()
|
||||
+ verification.verified.heap_size_of_children(),
|
||||
// TODO: https://github.com/servo/heapsize/pull/50
|
||||
//+ self.processing.read().unwrap().heap_size_of_children(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_garbage(&self) {
|
||||
{
|
||||
let mut verification = self.verification.lock().unwrap();
|
||||
verification.unverified.shrink_to_fit();
|
||||
verification.verifying.shrink_to_fit();
|
||||
verification.verified.shrink_to_fit();
|
||||
}
|
||||
self.processing.write().unwrap().shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
impl MayPanic for BlockQueue {
|
||||
@@ -373,7 +425,7 @@ mod tests {
|
||||
fn get_test_queue() -> BlockQueue {
|
||||
let spec = get_test_spec();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
BlockQueue::new(Arc::new(engine), IoChannel::disconnected())
|
||||
BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -381,7 +433,7 @@ mod tests {
|
||||
// TODO better test
|
||||
let spec = Spec::new_test();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let _ = BlockQueue::new(Arc::new(engine), IoChannel::disconnected());
|
||||
let _ = BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -437,4 +489,19 @@ mod tests {
|
||||
|
||||
assert!(queue.queue_info().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_limit() {
|
||||
let spec = get_test_spec();
|
||||
let engine = spec.to_engine().unwrap();
|
||||
let mut config = BlockQueueConfig::default();
|
||||
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
|
||||
let mut queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());
|
||||
assert!(!queue.queue_info().is_full());
|
||||
let mut blocks = get_good_dummy_block_seq(50);
|
||||
for b in blocks.drain(..) {
|
||||
queue.import_block(b).unwrap();
|
||||
}
|
||||
assert!(queue.queue_info().is_full());
|
||||
}
|
||||
}
|
||||
|
||||
29
ethcore/src/blockchain/best_block.rs
Normal file
29
ethcore/src/blockchain/best_block.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::{U256,H256};
|
||||
use header::BlockNumber;
|
||||
|
||||
/// Best block info.
|
||||
#[derive(Default)]
|
||||
pub struct BestBlock {
|
||||
/// Best block hash.
|
||||
pub hash: H256,
|
||||
/// Best block number.
|
||||
pub number: BlockNumber,
|
||||
/// Best block total difficulty.
|
||||
pub total_difficulty: U256
|
||||
}
|
||||
47
ethcore/src/blockchain/block_info.rs
Normal file
47
ethcore/src/blockchain/block_info.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::{U256,H256};
|
||||
use header::BlockNumber;
|
||||
|
||||
/// Brief info about inserted block.
|
||||
pub struct BlockInfo {
|
||||
/// Block hash.
|
||||
pub hash: H256,
|
||||
/// Block number.
|
||||
pub number: BlockNumber,
|
||||
/// Total block difficulty.
|
||||
pub total_difficulty: U256,
|
||||
/// Block location in blockchain.
|
||||
pub location: BlockLocation
|
||||
}
|
||||
|
||||
/// Describes location of newly inserted block.
|
||||
pub enum BlockLocation {
|
||||
/// It's part of the canon chain.
|
||||
CanonChain,
|
||||
/// It's not a part of the canon chain.
|
||||
Branch,
|
||||
/// It's part of the fork which should become canon chain,
|
||||
/// because it's total difficulty is higher than current
|
||||
/// canon chain difficulty.
|
||||
BranchBecomingCanonChain {
|
||||
/// Hash of the newest common ancestor with old canon chain.
|
||||
ancestor: H256,
|
||||
/// Hashes of the blocks between ancestor and this block.
|
||||
route: Vec<H256>
|
||||
}
|
||||
}
|
||||
@@ -23,109 +23,30 @@ use transaction::*;
|
||||
use views::*;
|
||||
use receipt::Receipt;
|
||||
use chainfilter::{ChainFilter, BloomIndex, FilterDataSource};
|
||||
use blockchain::block_info::{BlockInfo, BlockLocation};
|
||||
use blockchain::best_block::BestBlock;
|
||||
use blockchain::bloom_indexer::BloomIndexer;
|
||||
use blockchain::tree_route::TreeRoute;
|
||||
use blockchain::update::ExtrasUpdate;
|
||||
use blockchain::CacheSize;
|
||||
|
||||
const BLOOM_INDEX_SIZE: usize = 16;
|
||||
const BLOOM_LEVELS: u8 = 3;
|
||||
|
||||
/// Represents a tree route between `from` block and `to` block:
|
||||
pub struct TreeRoute {
|
||||
/// A vector of hashes of all blocks, ordered from `from` to `to`.
|
||||
pub blocks: Vec<H256>,
|
||||
/// Best common ancestor of these blocks.
|
||||
pub ancestor: H256,
|
||||
/// An index where best common ancestor would be.
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
/// Represents blockchain's in-memory cache size in bytes.
|
||||
/// Blockchain configuration.
|
||||
#[derive(Debug)]
|
||||
pub struct CacheSize {
|
||||
/// Blocks cache size.
|
||||
pub blocks: usize,
|
||||
/// BlockDetails cache size.
|
||||
pub block_details: usize,
|
||||
/// Transaction addresses cache size.
|
||||
pub transaction_addresses: usize,
|
||||
/// Logs cache size.
|
||||
pub block_logs: usize,
|
||||
/// Blooms cache size.
|
||||
pub blocks_blooms: usize,
|
||||
/// Block receipts size.
|
||||
pub block_receipts: usize,
|
||||
pub struct BlockChainConfig {
|
||||
/// Preferred cache size in bytes.
|
||||
pub pref_cache_size: usize,
|
||||
/// Maximum cache size in bytes.
|
||||
pub max_cache_size: usize,
|
||||
}
|
||||
|
||||
struct BloomIndexer {
|
||||
index_size: usize,
|
||||
levels: u8,
|
||||
}
|
||||
|
||||
impl BloomIndexer {
|
||||
fn new(index_size: usize, levels: u8) -> Self {
|
||||
BloomIndexer {
|
||||
index_size: index_size,
|
||||
levels: levels
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates bloom's position in database.
|
||||
fn location(&self, bloom_index: &BloomIndex) -> BlocksBloomLocation {
|
||||
use std::{mem, ptr};
|
||||
|
||||
let hash = unsafe {
|
||||
let mut hash: H256 = mem::zeroed();
|
||||
ptr::copy(&[bloom_index.index / self.index_size] as *const usize as *const u8, hash.as_mut_ptr(), 8);
|
||||
hash[8] = bloom_index.level;
|
||||
hash.reverse();
|
||||
hash
|
||||
};
|
||||
|
||||
BlocksBloomLocation {
|
||||
hash: hash,
|
||||
index: bloom_index.index % self.index_size
|
||||
}
|
||||
}
|
||||
|
||||
fn index_size(&self) -> usize {
|
||||
self.index_size
|
||||
}
|
||||
|
||||
fn levels(&self) -> u8 {
|
||||
self.levels
|
||||
}
|
||||
}
|
||||
|
||||
/// Blockchain update info.
|
||||
struct ExtrasUpdate {
|
||||
/// Block hash.
|
||||
hash: H256,
|
||||
/// DB update batch.
|
||||
batch: DBTransaction,
|
||||
/// Inserted block familial details.
|
||||
details: BlockDetails,
|
||||
/// New best block (if it has changed).
|
||||
new_best: Option<BestBlock>,
|
||||
/// Changed blocks bloom location hashes.
|
||||
bloom_hashes: HashSet<H256>,
|
||||
}
|
||||
|
||||
impl CacheSize {
|
||||
/// Total amount used by the cache.
|
||||
fn total(&self) -> usize { self.blocks + self.block_details + self.transaction_addresses + self.block_logs + self.blocks_blooms }
|
||||
}
|
||||
|
||||
/// Information about best block gathered together
|
||||
struct BestBlock {
|
||||
pub hash: H256,
|
||||
pub number: BlockNumber,
|
||||
pub total_difficulty: U256
|
||||
}
|
||||
|
||||
impl BestBlock {
|
||||
fn new() -> BestBlock {
|
||||
BestBlock {
|
||||
hash: H256::new(),
|
||||
number: 0,
|
||||
total_difficulty: U256::from(0)
|
||||
impl Default for BlockChainConfig {
|
||||
fn default() -> Self {
|
||||
BlockChainConfig {
|
||||
pref_cache_size: 1 << 14,
|
||||
max_cache_size: 1 << 20,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,33 +229,7 @@ const COLLECTION_QUEUE_SIZE: usize = 8;
|
||||
|
||||
impl BlockChain {
|
||||
/// Create new instance of blockchain from given Genesis
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate ethcore_util as util;
|
||||
/// extern crate ethcore;
|
||||
/// use std::env;
|
||||
/// use std::str::FromStr;
|
||||
/// use ethcore::spec::*;
|
||||
/// use ethcore::blockchain::*;
|
||||
/// use ethcore::ethereum;
|
||||
/// use util::hash::*;
|
||||
/// use util::uint::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let spec = ethereum::new_frontier();
|
||||
///
|
||||
/// let mut dir = env::temp_dir();
|
||||
/// dir.push(H32::random().hex());
|
||||
///
|
||||
/// let bc = BlockChain::new(&spec.genesis_block(), &dir);
|
||||
///
|
||||
/// let genesis_hash = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3";
|
||||
/// assert_eq!(bc.genesis_hash(), H256::from_str(genesis_hash).unwrap());
|
||||
/// assert!(bc.is_known(&bc.genesis_hash()));
|
||||
/// assert_eq!(bc.genesis_hash(), bc.block_hash(0).unwrap());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new(genesis: &[u8], path: &Path) -> BlockChain {
|
||||
pub fn new(config: BlockChainConfig, genesis: &[u8], path: &Path) -> BlockChain {
|
||||
// open extras db
|
||||
let mut extras_path = path.to_path_buf();
|
||||
extras_path.push("extras");
|
||||
@@ -349,9 +244,9 @@ impl BlockChain {
|
||||
(0..COLLECTION_QUEUE_SIZE).foreach(|_| cache_man.cache_usage.push_back(HashSet::new()));
|
||||
|
||||
let bc = BlockChain {
|
||||
pref_cache_size: 1 << 14,
|
||||
max_cache_size: 1 << 20,
|
||||
best_block: RwLock::new(BestBlock::new()),
|
||||
pref_cache_size: config.pref_cache_size,
|
||||
max_cache_size: config.max_cache_size,
|
||||
best_block: RwLock::new(BestBlock::default()),
|
||||
blocks: RwLock::new(HashMap::new()),
|
||||
block_details: RwLock::new(HashMap::new()),
|
||||
block_hashes: RwLock::new(HashMap::new()),
|
||||
@@ -452,40 +347,26 @@ impl BlockChain {
|
||||
/// ```json
|
||||
/// { blocks: [B4, B3, A3, A4], ancestor: A2, index: 2 }
|
||||
/// ```
|
||||
pub fn tree_route(&self, from: H256, to: H256) -> Option<TreeRoute> {
|
||||
let from_details = match self.block_details(&from) {
|
||||
Some(h) => h,
|
||||
None => return None,
|
||||
};
|
||||
let to_details = match self.block_details(&to) {
|
||||
Some(h) => h,
|
||||
None => return None,
|
||||
};
|
||||
Some(self.tree_route_aux((&from_details, &from), (&to_details, &to)))
|
||||
}
|
||||
|
||||
/// Similar to `tree_route` function, but can be used to return a route
|
||||
/// between blocks which may not be in database yet.
|
||||
fn tree_route_aux(&self, from: (&BlockDetails, &H256), to: (&BlockDetails, &H256)) -> TreeRoute {
|
||||
pub fn tree_route(&self, from: H256, to: H256) -> TreeRoute {
|
||||
let mut from_branch = vec![];
|
||||
let mut to_branch = vec![];
|
||||
|
||||
let mut from_details = from.0.clone();
|
||||
let mut to_details = to.0.clone();
|
||||
let mut current_from = from.1.clone();
|
||||
let mut current_to = to.1.clone();
|
||||
let mut from_details = self.block_details(&from).expect(&format!("0. Expected to find details for block {:?}", from));
|
||||
let mut to_details = self.block_details(&to).expect(&format!("1. Expected to find details for block {:?}", to));
|
||||
let mut current_from = from;
|
||||
let mut current_to = to;
|
||||
|
||||
// reset from && to to the same level
|
||||
while from_details.number > to_details.number {
|
||||
from_branch.push(current_from);
|
||||
current_from = from_details.parent.clone();
|
||||
from_details = self.block_details(&from_details.parent).expect(&format!("1. Expected to find details for block {:?}", from_details.parent));
|
||||
from_details = self.block_details(&from_details.parent).expect(&format!("2. Expected to find details for block {:?}", from_details.parent));
|
||||
}
|
||||
|
||||
while to_details.number > from_details.number {
|
||||
to_branch.push(current_to);
|
||||
current_to = to_details.parent.clone();
|
||||
to_details = self.block_details(&to_details.parent).expect(&format!("2. Expected to find details for block {:?}", to_details.parent));
|
||||
to_details = self.block_details(&to_details.parent).expect(&format!("3. Expected to find details for block {:?}", to_details.parent));
|
||||
}
|
||||
|
||||
assert_eq!(from_details.number, to_details.number);
|
||||
@@ -494,11 +375,11 @@ impl BlockChain {
|
||||
while current_from != current_to {
|
||||
from_branch.push(current_from);
|
||||
current_from = from_details.parent.clone();
|
||||
from_details = self.block_details(&from_details.parent).expect(&format!("3. Expected to find details for block {:?}", from_details.parent));
|
||||
from_details = self.block_details(&from_details.parent).expect(&format!("4. Expected to find details for block {:?}", from_details.parent));
|
||||
|
||||
to_branch.push(current_to);
|
||||
current_to = to_details.parent.clone();
|
||||
to_details = self.block_details(&to_details.parent).expect(&format!("4. Expected to find details for block {:?}", from_details.parent));
|
||||
to_details = self.block_details(&to_details.parent).expect(&format!("5. Expected to find details for block {:?}", from_details.parent));
|
||||
}
|
||||
|
||||
let index = from_branch.len();
|
||||
@@ -527,165 +408,237 @@ impl BlockChain {
|
||||
|
||||
// store block in db
|
||||
self.blocks_db.put(&hash, &bytes).unwrap();
|
||||
let update = self.block_to_extras_update(bytes, receipts);
|
||||
self.apply_update(update);
|
||||
|
||||
let info = self.block_info(bytes);
|
||||
|
||||
self.apply_update(ExtrasUpdate {
|
||||
block_hashes: self.prepare_block_hashes_update(bytes, &info),
|
||||
block_details: self.prepare_block_details_update(bytes, &info),
|
||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||
info: info
|
||||
});
|
||||
}
|
||||
|
||||
/// Applies extras update.
|
||||
fn apply_update(&self, update: ExtrasUpdate) {
|
||||
let batch = DBTransaction::new();
|
||||
batch.put(b"best", &update.info.hash).unwrap();
|
||||
|
||||
// update best block
|
||||
let mut best_block = self.best_block.write().unwrap();
|
||||
if let Some(b) = update.new_best {
|
||||
*best_block = b;
|
||||
match update.info.location {
|
||||
BlockLocation::Branch => (),
|
||||
_ => {
|
||||
*best_block = BestBlock {
|
||||
hash: update.info.hash,
|
||||
number: update.info.number,
|
||||
total_difficulty: update.info.total_difficulty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// update details cache
|
||||
let mut write_details = self.block_details.write().unwrap();
|
||||
write_details.remove(&update.details.parent);
|
||||
write_details.insert(update.hash.clone(), update.details);
|
||||
self.note_used(CacheID::Block(update.hash));
|
||||
let mut write_hashes = self.block_hashes.write().unwrap();
|
||||
for (number, hash) in &update.block_hashes {
|
||||
batch.put_extras(number, hash);
|
||||
write_hashes.remove(number);
|
||||
}
|
||||
|
||||
let mut write_details = self.block_details.write().unwrap();
|
||||
for (hash, details) in update.block_details.into_iter() {
|
||||
batch.put_extras(&hash, &details);
|
||||
write_details.insert(hash, details);
|
||||
}
|
||||
|
||||
let mut write_receipts = self.block_receipts.write().unwrap();
|
||||
for (hash, receipt) in &update.block_receipts {
|
||||
batch.put_extras(hash, receipt);
|
||||
write_receipts.remove(hash);
|
||||
}
|
||||
|
||||
let mut write_txs = self.transaction_addresses.write().unwrap();
|
||||
for (hash, tx_address) in &update.transactions_addresses {
|
||||
batch.put_extras(hash, tx_address);
|
||||
write_txs.remove(hash);
|
||||
}
|
||||
|
||||
// update blocks blooms cache
|
||||
let mut write_blocks_blooms = self.blocks_blooms.write().unwrap();
|
||||
for bloom_hash in &update.bloom_hashes {
|
||||
for (bloom_hash, blocks_bloom) in &update.blocks_blooms {
|
||||
batch.put_extras(bloom_hash, blocks_bloom);
|
||||
write_blocks_blooms.remove(bloom_hash);
|
||||
}
|
||||
|
||||
// update extras database
|
||||
self.extras_db.write(update.batch).unwrap();
|
||||
self.extras_db.write(batch).unwrap();
|
||||
}
|
||||
|
||||
/// Transforms block into WriteBatch that may be written into database
|
||||
/// Additionally, if it's new best block it returns new best block object.
|
||||
fn block_to_extras_update(&self, bytes: &[u8], receipts: Vec<Receipt>) -> ExtrasUpdate {
|
||||
// create views onto rlp
|
||||
let block = BlockView::new(bytes);
|
||||
/// Get inserted block info which is critical to preapre extras updates.
|
||||
fn block_info(&self, block_bytes: &[u8]) -> BlockInfo {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header_view();
|
||||
|
||||
// prepare variables
|
||||
let hash = block.sha3();
|
||||
let mut parent_details = self.block_details(&header.parent_hash()).expect(format!("Invalid parent hash: {:?}", header.parent_hash()).as_ref());
|
||||
let number = header.number();
|
||||
let parent_hash = header.parent_hash();
|
||||
let parent_details = self.block_details(&parent_hash).expect(format!("Invalid parent hash: {:?}", parent_hash).as_ref());
|
||||
let total_difficulty = parent_details.total_difficulty + header.difficulty();
|
||||
let is_new_best = total_difficulty > self.best_block_total_difficulty();
|
||||
|
||||
BlockInfo {
|
||||
hash: hash,
|
||||
number: number,
|
||||
total_difficulty: total_difficulty,
|
||||
location: if is_new_best {
|
||||
// on new best block we need to make sure that all ancestors
|
||||
// are moved to "canon chain"
|
||||
// find the route between old best block and the new one
|
||||
let best_hash = self.best_block_hash();
|
||||
let route = self.tree_route(best_hash, parent_hash);
|
||||
|
||||
assert_eq!(number, parent_details.number + 1);
|
||||
|
||||
match route.blocks.len() {
|
||||
0 => BlockLocation::CanonChain,
|
||||
_ => BlockLocation::BranchBecomingCanonChain {
|
||||
ancestor: route.ancestor,
|
||||
route: route.blocks.into_iter().skip(route.index).collect()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BlockLocation::Branch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function returns modified block hashes.
|
||||
fn prepare_block_hashes_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<BlockNumber, H256> {
|
||||
let mut block_hashes = HashMap::new();
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header_view();
|
||||
let number = header.number();
|
||||
|
||||
match info.location {
|
||||
BlockLocation::Branch => (),
|
||||
BlockLocation::CanonChain => {
|
||||
block_hashes.insert(number, info.hash.clone());
|
||||
},
|
||||
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => {
|
||||
let ancestor_number = self.block_number(ancestor).unwrap();
|
||||
let start_number = ancestor_number + 1;
|
||||
|
||||
for (index, hash) in route.iter().cloned().enumerate() {
|
||||
block_hashes.insert(start_number + index as BlockNumber, hash);
|
||||
}
|
||||
|
||||
block_hashes.insert(number, info.hash.clone());
|
||||
}
|
||||
}
|
||||
|
||||
block_hashes
|
||||
}
|
||||
|
||||
/// This function returns modified block details.
|
||||
fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, BlockDetails> {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header_view();
|
||||
let parent_hash = header.parent_hash();
|
||||
|
||||
// update parent
|
||||
let mut parent_details = self.block_details(&parent_hash).expect(format!("Invalid parent hash: {:?}", parent_hash).as_ref());
|
||||
parent_details.children.push(info.hash.clone());
|
||||
|
||||
// create current block details
|
||||
let details = BlockDetails {
|
||||
number: header.number(),
|
||||
total_difficulty: total_difficulty,
|
||||
total_difficulty: info.total_difficulty,
|
||||
parent: parent_hash.clone(),
|
||||
children: vec![]
|
||||
};
|
||||
|
||||
// prepare the batch
|
||||
let batch = DBTransaction::new();
|
||||
// write to batch
|
||||
let mut block_details = HashMap::new();
|
||||
block_details.insert(parent_hash, parent_details);
|
||||
block_details.insert(info.hash.clone(), details);
|
||||
block_details
|
||||
}
|
||||
|
||||
// insert new block details
|
||||
batch.put_extras(&hash, &details);
|
||||
/// This function returns modified block receipts.
|
||||
fn prepare_block_receipts_update(&self, receipts: Vec<Receipt>, info: &BlockInfo) -> HashMap<H256, BlockReceipts> {
|
||||
let mut block_receipts = HashMap::new();
|
||||
block_receipts.insert(info.hash.clone(), BlockReceipts::new(receipts));
|
||||
block_receipts
|
||||
}
|
||||
|
||||
// update parent details
|
||||
parent_details.children.push(hash.clone());
|
||||
batch.put_extras(&parent_hash, &parent_details);
|
||||
/// This function returns modified transaction addresses.
|
||||
fn prepare_transaction_addresses_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, TransactionAddress> {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let transaction_hashes = block.transaction_hashes();
|
||||
|
||||
// update transaction addresses
|
||||
for (i, tx_hash) in block.transaction_hashes().iter().enumerate() {
|
||||
batch.put_extras(tx_hash, &TransactionAddress {
|
||||
block_hash: hash.clone(),
|
||||
index: i
|
||||
});
|
||||
}
|
||||
transaction_hashes.into_iter()
|
||||
.enumerate()
|
||||
.fold(HashMap::new(), |mut acc, (i ,tx_hash)| {
|
||||
acc.insert(tx_hash, TransactionAddress {
|
||||
block_hash: info.hash.clone(),
|
||||
index: i
|
||||
});
|
||||
acc
|
||||
})
|
||||
}
|
||||
|
||||
// update block receipts
|
||||
batch.put_extras(&hash, &BlockReceipts::new(receipts));
|
||||
/// This functions returns modified blocks blooms.
|
||||
///
|
||||
/// To accelerate blooms lookups, blomms are stored in multiple
|
||||
/// layers (BLOOM_LEVELS, currently 3).
|
||||
/// ChainFilter is responsible for building and rebuilding these layers.
|
||||
/// It returns them in HashMap, where values are Blooms and
|
||||
/// keys are BloomIndexes. BloomIndex represents bloom location on one
|
||||
/// of these layers.
|
||||
///
|
||||
/// To reduce number of queries to databse, block blooms are stored
|
||||
/// in BlocksBlooms structure which contains info about several
|
||||
/// (BLOOM_INDEX_SIZE, currently 16) consecutive blocks blooms.
|
||||
///
|
||||
/// Later, BloomIndexer is used to map bloom location on filter layer (BloomIndex)
|
||||
/// to bloom location in database (BlocksBloomLocation).
|
||||
///
|
||||
fn prepare_block_blooms_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, BlocksBlooms> {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header_view();
|
||||
|
||||
// if it's not new best block, just return
|
||||
if !is_new_best {
|
||||
return ExtrasUpdate {
|
||||
hash: hash.clone(),
|
||||
batch: batch,
|
||||
details: details,
|
||||
new_best: None,
|
||||
bloom_hashes: HashSet::new()
|
||||
};
|
||||
}
|
||||
|
||||
// if its new best block we need to make sure that all ancestors
|
||||
// are moved to "canon chain"
|
||||
// find the route between old best block and the new one
|
||||
let best_hash = self.best_block_hash();
|
||||
let best_details = self.block_details(&best_hash).expect("best block hash is invalid!");
|
||||
let route = self.tree_route_aux((&best_details, &best_hash), (&details, &hash));
|
||||
|
||||
let modified_blooms;
|
||||
|
||||
match route.blocks.len() {
|
||||
// its our parent
|
||||
1 => {
|
||||
batch.put_extras(&header.number(), &hash);
|
||||
|
||||
// update block blooms
|
||||
modified_blooms = ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
||||
.add_bloom(&header.log_bloom(), header.number() as usize);
|
||||
let modified_blooms = match info.location {
|
||||
BlockLocation::Branch => HashMap::new(),
|
||||
BlockLocation::CanonChain => {
|
||||
ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
||||
.add_bloom(&header.log_bloom(), header.number() as usize)
|
||||
},
|
||||
// it is a fork
|
||||
i if i > 1 => {
|
||||
let ancestor_number = self.block_number(&route.ancestor).unwrap();
|
||||
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => {
|
||||
let ancestor_number = self.block_number(ancestor).unwrap();
|
||||
let start_number = ancestor_number + 1;
|
||||
for (index, hash) in route.blocks.iter().skip(route.index).enumerate() {
|
||||
batch.put_extras(&(start_number + index as BlockNumber), hash);
|
||||
}
|
||||
|
||||
// get all blocks that are not part of canon chain (TODO: optimize it to one query)
|
||||
let blooms: Vec<H2048> = route.blocks.iter()
|
||||
.skip(route.index)
|
||||
let mut blooms: Vec<H2048> = route.iter()
|
||||
.map(|hash| self.block(hash).unwrap())
|
||||
.map(|bytes| BlockView::new(&bytes).header_view().log_bloom())
|
||||
.collect();
|
||||
|
||||
// reset blooms chain head
|
||||
modified_blooms = ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
||||
.reset_chain_head(&blooms, start_number as usize, self.best_block_number() as usize);
|
||||
},
|
||||
// route.blocks.len() could be 0 only if inserted block is best block,
|
||||
// and this is not possible at this stage
|
||||
_ => { unreachable!(); }
|
||||
blooms.push(header.log_bloom());
|
||||
|
||||
ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
||||
.reset_chain_head(&blooms, start_number as usize, self.best_block_number() as usize)
|
||||
}
|
||||
};
|
||||
|
||||
let bloom_hashes = modified_blooms.iter()
|
||||
.map(|(bloom_index, _)| self.bloom_indexer.location(&bloom_index).hash)
|
||||
.collect();
|
||||
|
||||
for (bloom_hash, blocks_blooms) in modified_blooms.into_iter()
|
||||
modified_blooms.into_iter()
|
||||
.fold(HashMap::new(), | mut acc, (bloom_index, bloom) | {
|
||||
{
|
||||
let location = self.bloom_indexer.location(&bloom_index);
|
||||
let mut blocks_blooms = acc
|
||||
.entry(location.hash.clone())
|
||||
.or_insert_with(|| self.blocks_blooms(&location.hash).unwrap_or_else(BlocksBlooms::new));
|
||||
assert_eq!(self.bloom_indexer.index_size, blocks_blooms.blooms.len());
|
||||
assert_eq!(self.bloom_indexer.index_size(), blocks_blooms.blooms.len());
|
||||
blocks_blooms.blooms[location.index] = bloom;
|
||||
}
|
||||
acc
|
||||
}) {
|
||||
batch.put_extras(&bloom_hash, &blocks_blooms);
|
||||
}
|
||||
|
||||
// this is new best block
|
||||
batch.put(b"best", &hash).unwrap();
|
||||
|
||||
let best_block = BestBlock {
|
||||
hash: hash.clone(),
|
||||
number: header.number(),
|
||||
total_difficulty: total_difficulty
|
||||
};
|
||||
|
||||
ExtrasUpdate {
|
||||
hash: hash,
|
||||
batch: batch,
|
||||
new_best: Some(best_block),
|
||||
details: details,
|
||||
bloom_hashes: bloom_hashes
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get best block hash.
|
||||
@@ -813,18 +766,23 @@ mod tests {
|
||||
use std::str::FromStr;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use util::hash::*;
|
||||
use blockchain::{BlockProvider, BlockChain};
|
||||
use util::sha3::Hashable;
|
||||
use blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
||||
use tests::helpers::*;
|
||||
use devtools::*;
|
||||
use blockchain::helpers::generators::{ChainGenerator, ChainIterator};
|
||||
use views::BlockView;
|
||||
|
||||
#[test]
|
||||
fn valid_tests_extra32() {
|
||||
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0925002c3260b44e44c3edebad1cc442142b03020209df1ab8bb86752edbd2cd7a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a0363659b251bf8b819179874c8cce7b9b983d7f3704cbb58a3b334431f7032871889032d09c281e1236c0c0".from_hex().unwrap();
|
||||
fn basic_blockchain_insert() {
|
||||
let mut canon_chain = ChainGenerator::default();
|
||||
let genesis = canon_chain.next().unwrap().rlp();
|
||||
let first = canon_chain.next().unwrap().rlp();
|
||||
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
||||
let first_hash = BlockView::new(&first).header_view().sha3();
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(&genesis, temp.as_path());
|
||||
|
||||
let genesis_hash = H256::from_str("3caa2203f3d7c136c0295ed128a7d31cea520b1ca5e27afe17d0853331798942").unwrap();
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||
|
||||
assert_eq!(bc.genesis_hash(), genesis_hash.clone());
|
||||
assert_eq!(bc.best_block_number(), 0);
|
||||
@@ -832,13 +790,9 @@ mod tests {
|
||||
assert_eq!(bc.block_hash(0), Some(genesis_hash.clone()));
|
||||
assert_eq!(bc.block_hash(1), None);
|
||||
assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![]);
|
||||
|
||||
let first = "f90285f90219a03caa2203f3d7c136c0295ed128a7d31cea520b1ca5e27afe17d0853331798942a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0bac6177a79e910c98d86ec31a09ae37ac2de15b754fd7bed1ba52362c49416bfa0d45893a296c1490a978e0bd321b5f2635d8280365c1fe9f693d65f233e791344a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b845627cb99a00102030405060708091011121314151617181920212223242526272829303132a08ccb2837fb2923bd97e8f2d08ea32012d6e34be018c73e49a0f98843e8f47d5d88e53be49fec01012ef866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba0cb088b8d2ff76a7b2c6616c9d02fb6b7a501afbf8b69d7180b09928a1b80b5e4a06448fe7476c606582039bb72a9f6f4b4fad18507b8dfbd00eebbe151cc573cd2c0".from_hex().unwrap();
|
||||
|
||||
bc.insert_block(&first, vec![]);
|
||||
|
||||
let first_hash = H256::from_str("a940e5af7d146b3b917c953a82e1966b906dace3a4e355b5b0a4560190357ea1").unwrap();
|
||||
|
||||
assert_eq!(bc.block_hash(0), Some(genesis_hash.clone()));
|
||||
assert_eq!(bc.best_block_number(), 1);
|
||||
assert_eq!(bc.best_block_hash(), first_hash.clone());
|
||||
@@ -851,23 +805,27 @@ mod tests {
|
||||
#[test]
|
||||
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
|
||||
fn test_small_fork() {
|
||||
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a059262c330941f3fe2a34d16d6e3c7b30d2ceb37c6a0e9a994c494ee1a61d2410885aa4c8bf8e56e264c0c0".from_hex().unwrap();
|
||||
let b1 = "f90261f901f9a05716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0cb52de543653d86ccd13ba3ddf8b052525b04231c6884a4db3188a184681d878a0e78628dd45a1f8dc495594d83b76c588a3ee67463260f8b7d4a42f574aeab29aa0e9244cf7503b79c03d3a099e07a80d2dbc77bb0b502d8a89d51ac0d68dd31313b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd882520884562791e580a051b3ecba4e3f2b49c11d42dd0851ec514b1be3138080f72a2b6e83868275d98f8877671f479c414b47f862f86080018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ca09e2709d7ec9bbe6b1bbbf0b2088828d14cd5e8642a1fee22dc74bfa89761a7f9a04bd8813dee4be989accdb708b1c2e325a7e9c695a8024e30e89d6c644e424747c0".from_hex().unwrap();
|
||||
let b2 = "f902ccf901f9a0437e51676ff10756fcfee5edd9159fa41dbcb1b2c592850450371cbecd54ee4fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
|
||||
let b3a = "f90261f901f9a036fde1253128666fcb95a5956da14a73489e988bb72738717ec1d31e1cee781aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a09dc4b1357c0b7b8108f8a098f4f9a1a274957bc9ebc22a9ae67ae81739e5b19ca007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefd882524d84562791eb80a074861666bd346c025889745c793b91ab9cd1e2ca19b5cf3c50d04d135b0a4d2b8809fe9587ea4cdc04f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba06fd84874d36d5de9e8e48978c03619b53a96b7ae0a4cd1ac118f103098b44801a00572596974dd7df4f9f69bd7456585618c568d8434ef6453391b89281ce12ae1c0".from_hex().unwrap();
|
||||
let b3b = "f90265f901f9a036fde1253128666fcb95a5956da14a73489e988bb72738717ec1d31e1cee781aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ab87dc338bfd6f662b1cd90bc0c9e40a1b2146a095312393c9e13ce3a5008b09a0e609b7a7d4b8a2403ec1268627ecd98783627246e8f1b26addb3ff504f76a054a0592fabf92476512952db3a69a2481a42912e668a1ee28c4c322e703bb665f8beb90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefd882a1f084562791ee80a0fe7098fa7e4ac5d637eea81fb23f8f78346826dbab430068dd9a249d0afa99818853e1a6b201ae3545f866f86402018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d0284c04062261ca06edc9ce8e7da4cc34067beb325dcad59e5655a164a5100a50bc3eb681b12c716a0abf9053d5de65b1be81fe50d327b84de685efbeecea34e7b747180a6c6023e44c0".from_hex().unwrap();
|
||||
let mut canon_chain = ChainGenerator::default();
|
||||
let genesis = canon_chain.next().unwrap().rlp();
|
||||
let blocks = canon_chain.clone().take(3).map(|block| block.rlp()).collect::<Vec<_>>();
|
||||
let fork = canon_chain.skip(2).fork(1).take(1).next().unwrap().rlp();
|
||||
|
||||
let genesis_hash = H256::from_str("5716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2").unwrap();
|
||||
let b1_hash = H256::from_str("437e51676ff10756fcfee5edd9159fa41dbcb1b2c592850450371cbecd54ee4f").unwrap();
|
||||
let b2_hash = H256::from_str("36fde1253128666fcb95a5956da14a73489e988bb72738717ec1d31e1cee781a").unwrap();
|
||||
let b3a_hash = H256::from_str("c208f88c9f5bf7e00840439742c12e5226d9752981f3ec0521bdcb6dd08af277").unwrap();
|
||||
let b3b_hash = H256::from_str("bf72270ae0d95c9ea39a6adab994793fddb8c10fba7391e26279474124605d54").unwrap();
|
||||
let b1 = blocks[0].clone();
|
||||
let b2 = blocks[1].clone();
|
||||
let b3a = blocks[2].clone();
|
||||
let b3b = fork;
|
||||
|
||||
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
||||
let b1_hash= BlockView::new(&b1).header_view().sha3();
|
||||
let b2_hash= BlockView::new(&b2).header_view().sha3();
|
||||
let b3a_hash= BlockView::new(&b3a).header_view().sha3();
|
||||
let b3b_hash= BlockView::new(&b3b).header_view().sha3();
|
||||
|
||||
// b3a is a part of canon chain, whereas b3b is part of sidechain
|
||||
let best_block_hash = H256::from_str("c208f88c9f5bf7e00840439742c12e5226d9752981f3ec0521bdcb6dd08af277").unwrap();
|
||||
let best_block_hash = b3a_hash.clone();
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(&genesis, temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||
bc.insert_block(&b1, vec![]);
|
||||
bc.insert_block(&b2, vec![]);
|
||||
bc.insert_block(&b3a, vec![]);
|
||||
@@ -886,52 +844,52 @@ mod tests {
|
||||
assert_eq!(bc.block_hash(3).unwrap(), b3a_hash);
|
||||
|
||||
// test trie route
|
||||
let r0_1 = bc.tree_route(genesis_hash.clone(), b1_hash.clone()).unwrap();
|
||||
let r0_1 = bc.tree_route(genesis_hash.clone(), b1_hash.clone());
|
||||
assert_eq!(r0_1.ancestor, genesis_hash);
|
||||
assert_eq!(r0_1.blocks, [b1_hash.clone()]);
|
||||
assert_eq!(r0_1.index, 0);
|
||||
|
||||
let r0_2 = bc.tree_route(genesis_hash.clone(), b2_hash.clone()).unwrap();
|
||||
let r0_2 = bc.tree_route(genesis_hash.clone(), b2_hash.clone());
|
||||
assert_eq!(r0_2.ancestor, genesis_hash);
|
||||
assert_eq!(r0_2.blocks, [b1_hash.clone(), b2_hash.clone()]);
|
||||
assert_eq!(r0_2.index, 0);
|
||||
|
||||
let r1_3a = bc.tree_route(b1_hash.clone(), b3a_hash.clone()).unwrap();
|
||||
let r1_3a = bc.tree_route(b1_hash.clone(), b3a_hash.clone());
|
||||
assert_eq!(r1_3a.ancestor, b1_hash);
|
||||
assert_eq!(r1_3a.blocks, [b2_hash.clone(), b3a_hash.clone()]);
|
||||
assert_eq!(r1_3a.index, 0);
|
||||
|
||||
let r1_3b = bc.tree_route(b1_hash.clone(), b3b_hash.clone()).unwrap();
|
||||
let r1_3b = bc.tree_route(b1_hash.clone(), b3b_hash.clone());
|
||||
assert_eq!(r1_3b.ancestor, b1_hash);
|
||||
assert_eq!(r1_3b.blocks, [b2_hash.clone(), b3b_hash.clone()]);
|
||||
assert_eq!(r1_3b.index, 0);
|
||||
|
||||
let r3a_3b = bc.tree_route(b3a_hash.clone(), b3b_hash.clone()).unwrap();
|
||||
let r3a_3b = bc.tree_route(b3a_hash.clone(), b3b_hash.clone());
|
||||
assert_eq!(r3a_3b.ancestor, b2_hash);
|
||||
assert_eq!(r3a_3b.blocks, [b3a_hash.clone(), b3b_hash.clone()]);
|
||||
assert_eq!(r3a_3b.index, 1);
|
||||
|
||||
let r1_0 = bc.tree_route(b1_hash.clone(), genesis_hash.clone()).unwrap();
|
||||
let r1_0 = bc.tree_route(b1_hash.clone(), genesis_hash.clone());
|
||||
assert_eq!(r1_0.ancestor, genesis_hash);
|
||||
assert_eq!(r1_0.blocks, [b1_hash.clone()]);
|
||||
assert_eq!(r1_0.index, 1);
|
||||
|
||||
let r2_0 = bc.tree_route(b2_hash.clone(), genesis_hash.clone()).unwrap();
|
||||
let r2_0 = bc.tree_route(b2_hash.clone(), genesis_hash.clone());
|
||||
assert_eq!(r2_0.ancestor, genesis_hash);
|
||||
assert_eq!(r2_0.blocks, [b2_hash.clone(), b1_hash.clone()]);
|
||||
assert_eq!(r2_0.index, 2);
|
||||
|
||||
let r3a_1 = bc.tree_route(b3a_hash.clone(), b1_hash.clone()).unwrap();
|
||||
let r3a_1 = bc.tree_route(b3a_hash.clone(), b1_hash.clone());
|
||||
assert_eq!(r3a_1.ancestor, b1_hash);
|
||||
assert_eq!(r3a_1.blocks, [b3a_hash.clone(), b2_hash.clone()]);
|
||||
assert_eq!(r3a_1.index, 2);
|
||||
|
||||
let r3b_1 = bc.tree_route(b3b_hash.clone(), b1_hash.clone()).unwrap();
|
||||
let r3b_1 = bc.tree_route(b3b_hash.clone(), b1_hash.clone());
|
||||
assert_eq!(r3b_1.ancestor, b1_hash);
|
||||
assert_eq!(r3b_1.blocks, [b3b_hash.clone(), b2_hash.clone()]);
|
||||
assert_eq!(r3b_1.index, 2);
|
||||
|
||||
let r3b_3a = bc.tree_route(b3b_hash.clone(), b3a_hash.clone()).unwrap();
|
||||
let r3b_3a = bc.tree_route(b3b_hash.clone(), b3a_hash.clone());
|
||||
assert_eq!(r3b_3a.ancestor, b2_hash);
|
||||
assert_eq!(r3b_3a.blocks, [b3b_hash.clone(), b3a_hash.clone()]);
|
||||
assert_eq!(r3b_3a.index, 1);
|
||||
@@ -946,14 +904,14 @@ mod tests {
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
{
|
||||
let bc = BlockChain::new(&genesis, temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||
assert_eq!(bc.best_block_hash(), genesis_hash);
|
||||
bc.insert_block(&b1, vec![]);
|
||||
assert_eq!(bc.best_block_hash(), b1_hash);
|
||||
}
|
||||
|
||||
{
|
||||
let bc = BlockChain::new(&genesis, temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||
assert_eq!(bc.best_block_hash(), b1_hash);
|
||||
}
|
||||
}
|
||||
@@ -1006,9 +964,9 @@ mod tests {
|
||||
let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap();
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(&genesis, temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||
bc.insert_block(&b1, vec![]);
|
||||
|
||||
|
||||
let transactions = bc.transactions(&b1_hash).unwrap();
|
||||
assert_eq!(transactions.len(), 7);
|
||||
for t in transactions {
|
||||
@@ -1028,7 +986,7 @@ mod tests {
|
||||
|
||||
// prepare for fork (b1a, child of genesis)
|
||||
let b1a = "f902ccf901f9a05716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000200000008000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000080000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004001832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
|
||||
|
||||
|
||||
// fork (b2a, child of b1a, with higher total difficulty)
|
||||
let b2a = "f902ccf901f9a0626b0774a7cbdad7bdce07b87d74b6fa91c1c359d725076215d76348f8399f56a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000200000008000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000080000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
|
||||
|
||||
@@ -1042,13 +1000,13 @@ mod tests {
|
||||
let bloom_ba = H2048::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(&genesis, temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
assert_eq!(blocks_b1, vec![]);
|
||||
assert_eq!(blocks_b2, vec![]);
|
||||
|
||||
|
||||
bc.insert_block(&b1, vec![]);
|
||||
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5);
|
||||
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5);
|
||||
@@ -1088,31 +1046,4 @@ mod tests {
|
||||
assert_eq!(blocks_b2, vec![2]);
|
||||
assert_eq!(blocks_ba, vec![3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bloom_indexer() {
|
||||
use chainfilter::BloomIndex;
|
||||
use blockchain::BloomIndexer;
|
||||
use extras::BlocksBloomLocation;
|
||||
|
||||
let bi = BloomIndexer::new(16, 3);
|
||||
|
||||
let index = BloomIndex::new(0, 0);
|
||||
assert_eq!(bi.location(&index), BlocksBloomLocation {
|
||||
hash: H256::new(),
|
||||
index: 0
|
||||
});
|
||||
|
||||
let index = BloomIndex::new(1, 0);
|
||||
assert_eq!(bi.location(&index), BlocksBloomLocation {
|
||||
hash: H256::from_str("0000000000000000000000000000000000000000000000010000000000000000").unwrap(),
|
||||
index: 0
|
||||
});
|
||||
|
||||
let index = BloomIndex::new(0, 299_999);
|
||||
assert_eq!(bi.location(&index), BlocksBloomLocation {
|
||||
hash: H256::from_str("000000000000000000000000000000000000000000000000000000000000493d").unwrap(),
|
||||
index: 15
|
||||
});
|
||||
}
|
||||
}
|
||||
102
ethcore/src/blockchain/bloom_indexer.rs
Normal file
102
ethcore/src/blockchain/bloom_indexer.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::H256;
|
||||
use chainfilter::BloomIndex;
|
||||
|
||||
/// Represents location of block bloom in extras database.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BlocksBloomLocation {
|
||||
/// Unique hash of BlocksBloom
|
||||
pub hash: H256,
|
||||
/// Index within BlocksBloom
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
/// Should be used to localize blocks blooms in extras database.
|
||||
pub struct BloomIndexer {
|
||||
index_size: usize,
|
||||
levels: u8,
|
||||
}
|
||||
|
||||
impl BloomIndexer {
|
||||
/// Plain constructor.
|
||||
pub fn new(index_size: usize, levels: u8) -> Self {
|
||||
BloomIndexer {
|
||||
index_size: index_size,
|
||||
levels: levels
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates bloom's position in database.
|
||||
pub fn location(&self, bloom_index: &BloomIndex) -> BlocksBloomLocation {
|
||||
use std::{mem, ptr};
|
||||
|
||||
let hash = unsafe {
|
||||
let mut hash: H256 = mem::zeroed();
|
||||
ptr::copy(&[bloom_index.index / self.index_size] as *const usize as *const u8, hash.as_mut_ptr(), 8);
|
||||
hash[8] = bloom_index.level;
|
||||
hash.reverse();
|
||||
hash
|
||||
};
|
||||
|
||||
BlocksBloomLocation {
|
||||
hash: hash,
|
||||
index: bloom_index.index % self.index_size
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns index size.
|
||||
pub fn index_size(&self) -> usize {
|
||||
self.index_size
|
||||
}
|
||||
|
||||
/// Returns number of cache levels.
|
||||
pub fn levels(&self) -> u8 {
|
||||
self.levels
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use util::hash::{H256, FixedHash};
|
||||
use chainfilter::BloomIndex;
|
||||
use blockchain::bloom_indexer::{BloomIndexer, BlocksBloomLocation};
|
||||
|
||||
#[test]
|
||||
fn test_bloom_indexer() {
|
||||
let bi = BloomIndexer::new(16, 3);
|
||||
|
||||
let index = BloomIndex::new(0, 0);
|
||||
assert_eq!(bi.location(&index), BlocksBloomLocation {
|
||||
hash: H256::new(),
|
||||
index: 0
|
||||
});
|
||||
|
||||
let index = BloomIndex::new(1, 0);
|
||||
assert_eq!(bi.location(&index), BlocksBloomLocation {
|
||||
hash: H256::from_str("0000000000000000000000000000000000000000000000010000000000000000").unwrap(),
|
||||
index: 0
|
||||
});
|
||||
|
||||
let index = BloomIndex::new(0, 299_999);
|
||||
assert_eq!(bi.location(&index), BlocksBloomLocation {
|
||||
hash: H256::from_str("000000000000000000000000000000000000000000000000000000000000493d").unwrap(),
|
||||
index: 15
|
||||
});
|
||||
}
|
||||
}
|
||||
37
ethcore/src/blockchain/cache.rs
Normal file
37
ethcore/src/blockchain/cache.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/// Represents blockchain's in-memory cache size in bytes.
|
||||
#[derive(Debug)]
|
||||
pub struct CacheSize {
|
||||
/// Blocks cache size.
|
||||
pub blocks: usize,
|
||||
/// BlockDetails cache size.
|
||||
pub block_details: usize,
|
||||
/// Transaction addresses cache size.
|
||||
pub transaction_addresses: usize,
|
||||
/// Logs cache size.
|
||||
pub block_logs: usize,
|
||||
/// Blooms cache size.
|
||||
pub blocks_blooms: usize,
|
||||
/// Block receipts size.
|
||||
pub block_receipts: usize,
|
||||
}
|
||||
|
||||
impl CacheSize {
|
||||
/// Total amount used by the cache.
|
||||
pub fn total(&self) -> usize { self.blocks + self.block_details + self.transaction_addresses + self.block_logs + self.blocks_blooms }
|
||||
}
|
||||
203
ethcore/src/blockchain/helpers/generators.rs
Normal file
203
ethcore/src/blockchain/helpers/generators.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::rlp::*;
|
||||
use util::hash::{H256, H2048};
|
||||
use util::uint::{U256};
|
||||
use util::bytes::Bytes;
|
||||
use header::{BlockNumber, Header};
|
||||
use transaction::SignedTransaction;
|
||||
|
||||
pub trait Forkable {
|
||||
fn fork(self, fork_number: usize) -> Self where Self: Sized;
|
||||
}
|
||||
|
||||
pub struct Fork<I> {
|
||||
iter: I,
|
||||
fork_number: usize,
|
||||
}
|
||||
|
||||
impl<I> Iterator for Fork<I> where I: Iterator, <I as Iterator>::Item: Forkable {
|
||||
type Item = <I as Iterator>::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|item| item.fork(self.fork_number))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WithBloom {
|
||||
fn with_bloom(self, bloom: H2048) -> Self where Self: Sized;
|
||||
}
|
||||
|
||||
pub struct Bloom<I> {
|
||||
iter: I,
|
||||
bloom: H2048,
|
||||
}
|
||||
|
||||
impl<I> Iterator for Bloom<I> where I: Iterator, <I as Iterator>::Item: WithBloom {
|
||||
type Item = <I as Iterator>::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|item| item.with_bloom(self.bloom.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Chain iterator interface.
|
||||
pub trait ChainIterator: Iterator {
|
||||
/// Should be called to create a fork of current iterator.
|
||||
/// Blocks generated by fork will have lower difficulty than current chain.
|
||||
fn fork(&mut self, fork_number: usize) -> Fork<Self> where Self: Sized;
|
||||
/// Should be called to make every consecutive block have given bloom.
|
||||
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self> where Self: Sized;
|
||||
}
|
||||
|
||||
impl<I> ChainIterator for I where I: Iterator + Sized + Clone {
|
||||
fn fork(&mut self, fork_number: usize) -> Fork<Self> {
|
||||
Fork {
|
||||
iter: self.clone(),
|
||||
fork_number: fork_number
|
||||
}
|
||||
}
|
||||
|
||||
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self> {
|
||||
Bloom {
|
||||
iter: self.clone(),
|
||||
bloom: bloom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper structure, used for encoding blocks.
|
||||
#[derive(Default)]
|
||||
pub struct Block {
|
||||
header: Header,
|
||||
transactions: Vec<SignedTransaction>,
|
||||
uncles: Vec<Header>
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub fn rlp(&self) -> Bytes {
|
||||
encode(self).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Block {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(3);
|
||||
s.append(&self.header);
|
||||
s.append(&self.transactions);
|
||||
s.append(&self.uncles);
|
||||
}
|
||||
}
|
||||
|
||||
impl Forkable for Block {
|
||||
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
||||
self.header.difficulty = self.header.difficulty - U256::from(fork_number);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl WithBloom for Block {
|
||||
fn with_bloom(mut self, bloom: H2048) -> Self where Self: Sized {
|
||||
self.header.log_bloom = bloom;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Blockchain generator.
|
||||
#[derive(Clone)]
|
||||
pub struct ChainGenerator {
|
||||
/// Next block number.
|
||||
number: BlockNumber,
|
||||
/// Next block parent hash.
|
||||
parent_hash: H256,
|
||||
/// Next block difficulty.
|
||||
difficulty: U256,
|
||||
}
|
||||
|
||||
impl ChainGenerator {
|
||||
fn prepare_block(&self) -> Block {
|
||||
let mut block = Block::default();
|
||||
block.header.parent_hash = self.parent_hash.clone();
|
||||
block.header.number = self.number;
|
||||
block.header.difficulty = self.difficulty;
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ChainGenerator {
|
||||
fn default() -> Self {
|
||||
ChainGenerator {
|
||||
number: 0,
|
||||
parent_hash: H256::default(),
|
||||
difficulty: U256::from(1000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ChainGenerator {
|
||||
type Item = Block;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let block = self.prepare_block();
|
||||
self.number += 1;
|
||||
self.parent_hash = block.header.hash();
|
||||
Some(block)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::hash::H256;
|
||||
use util::sha3::Hashable;
|
||||
use views::BlockView;
|
||||
use super::{ChainIterator, ChainGenerator};
|
||||
|
||||
#[test]
|
||||
fn canon_chain_generator() {
|
||||
let mut canon_chain = ChainGenerator::default();
|
||||
|
||||
let genesis_rlp = canon_chain.next().unwrap().rlp();
|
||||
let genesis = BlockView::new(&genesis_rlp);
|
||||
|
||||
assert_eq!(genesis.header_view().parent_hash(), H256::default());
|
||||
assert_eq!(genesis.header_view().number(), 0);
|
||||
|
||||
let b1_rlp = canon_chain.next().unwrap().rlp();
|
||||
let b1 = BlockView::new(&b1_rlp);
|
||||
|
||||
assert_eq!(b1.header_view().parent_hash(), genesis.header_view().sha3());
|
||||
assert_eq!(b1.header_view().number(), 1);
|
||||
|
||||
let mut fork_chain = canon_chain.fork(1);
|
||||
|
||||
let b2_rlp_fork = fork_chain.next().unwrap().rlp();
|
||||
let b2_fork = BlockView::new(&b2_rlp_fork);
|
||||
|
||||
assert_eq!(b2_fork.header_view().parent_hash(), b1.header_view().sha3());
|
||||
assert_eq!(b2_fork.header_view().number(), 2);
|
||||
|
||||
let b2_rlp = canon_chain.next().unwrap().rlp();
|
||||
let b2 = BlockView::new(&b2_rlp);
|
||||
|
||||
assert_eq!(b2.header_view().parent_hash(), b1.header_view().sha3());
|
||||
assert_eq!(b2.header_view().number(), 2);
|
||||
assert!(b2.header_view().difficulty() > b2_fork.header_view().difficulty());
|
||||
}
|
||||
}
|
||||
17
ethcore/src/blockchain/helpers/mod.rs
Normal file
17
ethcore/src/blockchain/helpers/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod generators;
|
||||
31
ethcore/src/blockchain/mod.rs
Normal file
31
ethcore/src/blockchain/mod.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Blockchain database.
|
||||
|
||||
pub mod blockchain;
|
||||
mod best_block;
|
||||
mod block_info;
|
||||
mod bloom_indexer;
|
||||
mod cache;
|
||||
mod tree_route;
|
||||
mod update;
|
||||
#[cfg(test)]
|
||||
mod helpers;
|
||||
|
||||
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
||||
pub use self::cache::CacheSize;
|
||||
pub use self::tree_route::TreeRoute;
|
||||
29
ethcore/src/blockchain/tree_route.rs
Normal file
29
ethcore/src/blockchain/tree_route.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::H256;
|
||||
|
||||
/// Represents a tree route between `from` block and `to` block:
|
||||
#[derive(Debug)]
|
||||
pub struct TreeRoute {
|
||||
/// A vector of hashes of all blocks, ordered from `from` to `to`.
|
||||
pub blocks: Vec<H256>,
|
||||
/// Best common ancestor of these blocks.
|
||||
pub ancestor: H256,
|
||||
/// An index where best common ancestor would be.
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
21
ethcore/src/blockchain/update.rs
Normal file
21
ethcore/src/blockchain/update.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use std::collections::HashMap;
|
||||
use util::numbers::H256;
|
||||
use header::BlockNumber;
|
||||
use blockchain::block_info::BlockInfo;
|
||||
use extras::{BlockDetails, BlockReceipts, TransactionAddress, BlocksBlooms};
|
||||
|
||||
/// Block extras update info.
|
||||
pub struct ExtrasUpdate {
|
||||
/// Block info.
|
||||
pub info: BlockInfo,
|
||||
/// Modified block hashes.
|
||||
pub block_hashes: HashMap<BlockNumber, H256>,
|
||||
/// Modified block details.
|
||||
pub block_details: HashMap<H256, BlockDetails>,
|
||||
/// Modified block receipts.
|
||||
pub block_receipts: HashMap<H256, BlockReceipts>,
|
||||
/// Modified transaction addresses.
|
||||
pub transactions_addresses: HashMap<H256, TransactionAddress>,
|
||||
/// Modified blocks blooms.
|
||||
pub blocks_blooms: HashMap<H256, BlocksBlooms>,
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use util::*;
|
||||
use util::panics::*;
|
||||
use blockchain::{BlockChain, BlockProvider, CacheSize};
|
||||
use blockchain::{BlockChain, BlockProvider};
|
||||
use views::BlockView;
|
||||
use error::*;
|
||||
use header::{BlockNumber, Header};
|
||||
@@ -26,7 +26,7 @@ use state::State;
|
||||
use spec::Spec;
|
||||
use engine::Engine;
|
||||
use views::HeaderView;
|
||||
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||
use block_queue::BlockQueue;
|
||||
use service::{NetSyncMessage, SyncMessage};
|
||||
use env_info::LastHashes;
|
||||
use verification::*;
|
||||
@@ -35,7 +35,8 @@ use transaction::LocalizedTransaction;
|
||||
use extras::TransactionAddress;
|
||||
use filter::Filter;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
pub use blockchain::TreeRoute;
|
||||
pub use block_queue::{BlockQueueConfig, BlockQueueInfo};
|
||||
pub use blockchain::{TreeRoute, BlockChainConfig, CacheSize as BlockChainCacheSize};
|
||||
|
||||
/// Uniquely identifies block.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -74,7 +75,16 @@ pub enum BlockStatus {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Information about the blockchain gthered together.
|
||||
/// Client configuration. Includes configs for all sub-systems.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ClientConfig {
|
||||
/// Block queue configuration.
|
||||
pub queue: BlockQueueConfig,
|
||||
/// Blockchain configuration.
|
||||
pub blockchain: BlockChainConfig,
|
||||
}
|
||||
|
||||
/// Information about the blockchain gathered together.
|
||||
#[derive(Debug)]
|
||||
pub struct BlockChainInfo {
|
||||
/// Blockchain difficulty.
|
||||
@@ -193,14 +203,14 @@ const CLIENT_DB_VER_STR: &'static str = "4.0";
|
||||
|
||||
impl Client {
|
||||
/// Create a new client with given spec and DB path.
|
||||
pub fn new(spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
|
||||
pub fn new(config: ClientConfig, spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
|
||||
let mut dir = path.to_path_buf();
|
||||
dir.push(H64::from(spec.genesis_header().hash()).hex());
|
||||
//TODO: sec/fat: pruned/full versioning
|
||||
dir.push(format!("v{}-sec-pruned", CLIENT_DB_VER_STR));
|
||||
let path = dir.as_path();
|
||||
let gb = spec.genesis_block();
|
||||
let chain = Arc::new(RwLock::new(BlockChain::new(&gb, path)));
|
||||
let chain = Arc::new(RwLock::new(BlockChain::new(config.blockchain, &gb, path)));
|
||||
let mut state_path = path.to_path_buf();
|
||||
state_path.push("state");
|
||||
|
||||
@@ -210,7 +220,7 @@ impl Client {
|
||||
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||
}
|
||||
|
||||
let block_queue = BlockQueue::new(engine.clone(), message_channel);
|
||||
let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel);
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
panic_handler.forward_from(&block_queue);
|
||||
|
||||
@@ -359,7 +369,7 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Get info on the cache.
|
||||
pub fn cache_info(&self) -> CacheSize {
|
||||
pub fn blockchain_cache_info(&self) -> BlockChainCacheSize {
|
||||
self.chain.read().unwrap().cache_size()
|
||||
}
|
||||
|
||||
@@ -371,6 +381,7 @@ impl Client {
|
||||
/// Tick the client.
|
||||
pub fn tick(&self) {
|
||||
self.chain.read().unwrap().collect_garbage();
|
||||
self.block_queue.read().unwrap().collect_garbage();
|
||||
}
|
||||
|
||||
/// Set up the cache behaviour.
|
||||
@@ -458,7 +469,11 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||
self.chain.read().unwrap().tree_route(from.clone(), to.clone())
|
||||
let chain = self.chain.read().unwrap();
|
||||
match chain.is_known(from) && chain.is_known(to) {
|
||||
true => Some(chain.tree_route(from.clone(), to.clone())),
|
||||
false => None
|
||||
}
|
||||
}
|
||||
|
||||
fn state_data(&self, _hash: &H256) -> Option<Bytes> {
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
//! Interface for Evm externalities.
|
||||
|
||||
use common::Bytes;
|
||||
use util::hash::*;
|
||||
use util::uint::*;
|
||||
use util::common::*;
|
||||
use evm::{Schedule, Error};
|
||||
use env_info::*;
|
||||
|
||||
@@ -60,22 +58,22 @@ pub trait Ext {
|
||||
fn blockhash(&self, number: &U256) -> H256;
|
||||
|
||||
/// Creates new contract.
|
||||
///
|
||||
///
|
||||
/// Returns gas_left and contract address if contract creation was succesfull.
|
||||
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult;
|
||||
|
||||
/// Message call.
|
||||
///
|
||||
///
|
||||
/// Returns Err, if we run out of gas.
|
||||
/// Otherwise returns call_result which contains gas left
|
||||
/// Otherwise returns call_result which contains gas left
|
||||
/// and true if subcall was successfull.
|
||||
fn call(&mut self,
|
||||
gas: &U256,
|
||||
sender_address: &Address,
|
||||
receive_address: &Address,
|
||||
fn call(&mut self,
|
||||
gas: &U256,
|
||||
sender_address: &Address,
|
||||
receive_address: &Address,
|
||||
value: Option<U256>,
|
||||
data: &[u8],
|
||||
code_address: &Address,
|
||||
data: &[u8],
|
||||
code_address: &Address,
|
||||
output: &mut [u8]) -> MessageCallResult;
|
||||
|
||||
/// Returns code at given address
|
||||
@@ -99,7 +97,7 @@ pub trait Ext {
|
||||
fn env_info(&self) -> &EnvInfo;
|
||||
|
||||
/// Returns current depth of execution.
|
||||
///
|
||||
///
|
||||
/// If contract A calls contract B, and contract B calls C,
|
||||
/// then A depth is 0, B is 1, C is 2 and so on.
|
||||
fn depth(&self) -> usize;
|
||||
|
||||
@@ -262,15 +262,6 @@ impl Encodable for BlocksBlooms {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents location of bloom in database.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BlocksBloomLocation {
|
||||
/// Unique hash of BlocksBloom
|
||||
pub hash: H256,
|
||||
/// Index within BlocksBloom
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
/// Represents address of certain transaction within block
|
||||
#[derive(Clone)]
|
||||
pub struct TransactionAddress {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::test_common::*;
|
||||
use client::{BlockChainClient,Client};
|
||||
use client::{BlockChainClient, Client, ClientConfig};
|
||||
use pod_state::*;
|
||||
use block::Block;
|
||||
use ethereum;
|
||||
@@ -53,7 +53,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
{
|
||||
let client = Client::new(spec, temp.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let client = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected()).unwrap();
|
||||
assert_eq!(client.chain_info().best_block_hash, genesis_hash);
|
||||
for (b, is_valid) in blocks.into_iter() {
|
||||
if Block::is_good(&b) {
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
#[macro_use] extern crate ethcore_util as util;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
extern crate rustc_serialize;
|
||||
extern crate heapsize;
|
||||
#[macro_use] extern crate heapsize;
|
||||
extern crate crypto;
|
||||
extern crate time;
|
||||
extern crate env_logger;
|
||||
@@ -86,8 +86,6 @@ extern crate crossbeam;
|
||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||
|
||||
pub mod block;
|
||||
pub mod blockchain;
|
||||
pub mod block_queue;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod ethereum;
|
||||
@@ -121,6 +119,8 @@ mod substate;
|
||||
mod executive;
|
||||
mod externalities;
|
||||
mod verification;
|
||||
mod block_queue;
|
||||
mod blockchain;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -20,7 +20,7 @@ use util::*;
|
||||
use util::panics::*;
|
||||
use spec::Spec;
|
||||
use error::*;
|
||||
use client::Client;
|
||||
use client::{Client, ClientConfig};
|
||||
|
||||
/// Message type for external and internal events
|
||||
#[derive(Clone)]
|
||||
@@ -48,14 +48,14 @@ pub struct ClientService {
|
||||
|
||||
impl ClientService {
|
||||
/// Start the service in a separate thread.
|
||||
pub fn start(spec: Spec, net_config: NetworkConfiguration, db_path: &Path) -> Result<ClientService, Error> {
|
||||
pub fn start(config: ClientConfig, spec: Spec, net_config: NetworkConfiguration, db_path: &Path) -> Result<ClientService, Error> {
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
let mut net_service = try!(NetworkService::start(net_config));
|
||||
panic_handler.forward_from(&net_service);
|
||||
|
||||
info!("Starting {}", net_service.host_info());
|
||||
info!("Configured for {} using {} engine", spec.name, spec.engine_name);
|
||||
let client = try!(Client::new(spec, db_path, net_service.io().channel()));
|
||||
let client = try!(Client::new(config, spec, db_path, net_service.io().channel()));
|
||||
panic_handler.forward_from(client.deref());
|
||||
let client_io = Arc::new(ClientIoHandler {
|
||||
client: client.clone()
|
||||
@@ -135,12 +135,13 @@ mod tests {
|
||||
use tests::helpers::*;
|
||||
use util::network::*;
|
||||
use devtools::*;
|
||||
use client::ClientConfig;
|
||||
|
||||
#[test]
|
||||
fn it_can_be_started() {
|
||||
let spec = get_test_spec();
|
||||
let temp_path = RandomTempPath::new();
|
||||
let service = ClientService::start(spec, NetworkConfiguration::new_with_port(40456), &temp_path.as_path());
|
||||
let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_with_port(40456), &temp_path.as_path());
|
||||
assert!(service.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ pub struct Spec {
|
||||
|
||||
/// Known nodes on the network in enode format.
|
||||
pub nodes: Vec<String>,
|
||||
/// Network ID
|
||||
pub network_id: U256,
|
||||
|
||||
/// Parameters concerning operation of the specific engine we're using.
|
||||
/// Maps the parameter name to an RLP-encoded value.
|
||||
@@ -120,6 +122,9 @@ impl Spec {
|
||||
/// Get the known knodes of the network in enode format.
|
||||
pub fn nodes(&self) -> &Vec<String> { &self.nodes }
|
||||
|
||||
/// Get the configured Network ID.
|
||||
pub fn network_id(&self) -> U256 { self.network_id }
|
||||
|
||||
/// Get the header of the genesis block.
|
||||
pub fn genesis_header(&self) -> Header {
|
||||
Header {
|
||||
@@ -250,6 +255,7 @@ impl FromJson for Spec {
|
||||
engine_name: json["engineName"].as_string().unwrap().to_owned(),
|
||||
engine_params: json_to_rlp_map(&json["params"]),
|
||||
nodes: nodes,
|
||||
network_id: U256::from_str(&json["params"]["networkID"].as_string().unwrap()[2..]).unwrap(),
|
||||
builtins: builtins,
|
||||
parent_hash: H256::from_str(&genesis["parentHash"].as_string().unwrap()[2..]).unwrap(),
|
||||
author: Address::from_str(&genesis["author"].as_string().unwrap()[2..]).unwrap(),
|
||||
|
||||
@@ -138,7 +138,7 @@ impl State {
|
||||
/// Create a new contract at address `contract`. If there is already an account at the address
|
||||
/// it will have its code reset, ready for `init_code()`.
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256) {
|
||||
self.insert_cache(&contract, Some(Account::new_contract(balance)));
|
||||
self.insert_cache(&contract, Some(Account::new_contract(balance, self.account_start_nonce)));
|
||||
}
|
||||
|
||||
/// Remove an existing account.
|
||||
@@ -204,7 +204,7 @@ impl State {
|
||||
/// Initialise the code of account `a` so that it is `value` for `key`.
|
||||
/// NOTE: Account should have been created with `new_contract`.
|
||||
pub fn init_code(&mut self, a: &Address, code: Bytes) {
|
||||
self.require_or_from(a, true, || Account::new_contract(U256::from(0u8)), |_|{}).init_code(code);
|
||||
self.require_or_from(a, true, || Account::new_contract(x!(0), self.account_start_nonce), |_|{}).init_code(code);
|
||||
}
|
||||
|
||||
/// Execute a given transaction.
|
||||
@@ -335,10 +335,9 @@ impl fmt::Debug for State {
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use util::hash::*;
|
||||
use util::common::*;
|
||||
use util::trie::*;
|
||||
use util::rlp::*;
|
||||
use util::uint::*;
|
||||
use account::*;
|
||||
use tests::helpers::*;
|
||||
use devtools::*;
|
||||
@@ -349,7 +348,7 @@ fn code_from_database() {
|
||||
let temp = RandomTempPath::new();
|
||||
let (root, db) = {
|
||||
let mut state = get_temp_state_in(temp.as_path());
|
||||
state.require_or_from(&a, false, ||Account::new_contract(U256::from(42u32)), |_|{});
|
||||
state.require_or_from(&a, false, ||Account::new_contract(x!(42), x!(0)), |_|{});
|
||||
state.init_code(&a, vec![1, 2, 3]);
|
||||
assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec()));
|
||||
state.commit();
|
||||
|
||||
@@ -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 client::{BlockChainClient, Client, BlockId};
|
||||
use client::{BlockChainClient, Client, ClientConfig, BlockId};
|
||||
use tests::helpers::*;
|
||||
use common::*;
|
||||
use devtools::*;
|
||||
@@ -22,14 +22,14 @@ use devtools::*;
|
||||
#[test]
|
||||
fn created() {
|
||||
let dir = RandomTempPath::new();
|
||||
let client_result = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected());
|
||||
let client_result = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected());
|
||||
assert!(client_result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imports_from_empty() {
|
||||
let dir = RandomTempPath::new();
|
||||
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
client.import_verified_blocks(&IoChannel::disconnected());
|
||||
client.flush_queue();
|
||||
}
|
||||
@@ -37,7 +37,7 @@ fn imports_from_empty() {
|
||||
#[test]
|
||||
fn imports_good_block() {
|
||||
let dir = RandomTempPath::new();
|
||||
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let good_block = get_good_dummy_block();
|
||||
if let Err(_) = client.import_block(good_block) {
|
||||
panic!("error importing block being good by definition");
|
||||
@@ -52,7 +52,7 @@ fn imports_good_block() {
|
||||
#[test]
|
||||
fn query_none_block() {
|
||||
let dir = RandomTempPath::new();
|
||||
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
|
||||
let non_existant = client.block_header(BlockId::Number(188));
|
||||
assert!(non_existant.is_none());
|
||||
@@ -104,5 +104,5 @@ fn can_collect_garbage() {
|
||||
let client_result = generate_dummy_client(100);
|
||||
let client = client_result.reference();
|
||||
client.tick();
|
||||
assert!(client.cache_info().blocks < 100 * 1024);
|
||||
assert!(client.blockchain_cache_info().blocks < 100 * 1024);
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use client::{BlockChainClient, Client};
|
||||
use client::{BlockChainClient, Client, ClientConfig};
|
||||
use common::*;
|
||||
use spec::*;
|
||||
use blockchain::{BlockChain};
|
||||
use blockchain::{BlockChain, BlockChainConfig};
|
||||
use state::*;
|
||||
use evm::{Schedule, Factory};
|
||||
use engine::*;
|
||||
@@ -134,7 +134,7 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTrans
|
||||
pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> {
|
||||
let dir = RandomTempPath::new();
|
||||
|
||||
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let state_root = test_engine.spec().genesis_header().state_root;
|
||||
@@ -172,7 +172,7 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
|
||||
|
||||
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
|
||||
let dir = RandomTempPath::new();
|
||||
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
|
||||
for block in &blocks {
|
||||
if let Err(_) = client.import_block(block.clone()) {
|
||||
panic!("panic importing block which is well-formed");
|
||||
@@ -189,7 +189,7 @@ pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<
|
||||
|
||||
pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockChain> {
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), temp.as_path());
|
||||
for block_order in 1..block_number {
|
||||
bc.insert_block(&create_unverifiable_block(block_order, bc.best_block_hash()), vec![]);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockCh
|
||||
|
||||
pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult<BlockChain> {
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), temp.as_path());
|
||||
for block_order in 1..block_number {
|
||||
bc.insert_block(&create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![]);
|
||||
}
|
||||
@@ -215,7 +215,7 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempRes
|
||||
|
||||
pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
|
||||
let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), temp.as_path());
|
||||
|
||||
GuardedTempResult::<BlockChain> {
|
||||
_temp: temp,
|
||||
@@ -250,6 +250,25 @@ pub fn get_temp_state_in(path: &Path) -> State {
|
||||
State::new(journal_db, U256::from(0u8))
|
||||
}
|
||||
|
||||
pub fn get_good_dummy_block_seq(count: usize) -> Vec<Bytes> {
|
||||
let test_spec = get_test_spec();
|
||||
let test_engine = test_spec.to_engine().unwrap();
|
||||
let mut parent = test_engine.spec().genesis_header().hash();
|
||||
let mut r = Vec::new();
|
||||
for i in 1 .. count + 1 {
|
||||
let mut block_header = Header::new();
|
||||
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
|
||||
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||
block_header.timestamp = i as u64;
|
||||
block_header.number = i as u64;
|
||||
block_header.parent_hash = parent;
|
||||
block_header.state_root = test_engine.spec().genesis_header().state_root;
|
||||
parent = block_header.hash();
|
||||
r.push(create_test_block(&block_header));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub fn get_good_dummy_block() -> Bytes {
|
||||
let mut block_header = Header::new();
|
||||
let test_spec = get_test_spec();
|
||||
|
||||
@@ -100,10 +100,10 @@ impl FromJson for SignedTransaction {
|
||||
v: match json.find("v") { Some(ref j) => u16::from_json(j) as u8, None => 0 },
|
||||
r: match json.find("r") { Some(j) => xjson!(j), None => x!(0) },
|
||||
s: match json.find("s") { Some(j) => xjson!(j), None => x!(0) },
|
||||
hash: RefCell::new(None),
|
||||
hash: Cell::new(None),
|
||||
sender: match json.find("sender") {
|
||||
Some(&Json::String(ref sender)) => RefCell::new(Some(address_from_hex(clean(sender)))),
|
||||
_ => RefCell::new(None),
|
||||
Some(&Json::String(ref sender)) => Cell::new(Some(address_from_hex(clean(sender)))),
|
||||
_ => Cell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,8 +127,8 @@ impl Transaction {
|
||||
r: r,
|
||||
s: s,
|
||||
v: v + 27,
|
||||
hash: RefCell::new(None),
|
||||
sender: RefCell::new(None)
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +140,8 @@ impl Transaction {
|
||||
r: U256::zero(),
|
||||
s: U256::zero(),
|
||||
v: 0,
|
||||
hash: RefCell::new(None),
|
||||
sender: RefCell::new(None)
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,9 +171,9 @@ pub struct SignedTransaction {
|
||||
/// The S field of the signature; helps describe the point on the curve.
|
||||
s: U256,
|
||||
/// Cached hash.
|
||||
hash: RefCell<Option<H256>>,
|
||||
hash: Cell<Option<H256>>,
|
||||
/// Cached sender.
|
||||
sender: RefCell<Option<Address>>
|
||||
sender: Cell<Option<Address>>,
|
||||
}
|
||||
|
||||
impl PartialEq for SignedTransaction {
|
||||
@@ -208,8 +208,8 @@ impl Decodable for SignedTransaction {
|
||||
v: try!(d.val_at(6)),
|
||||
r: try!(d.val_at(7)),
|
||||
s: try!(d.val_at(8)),
|
||||
hash: RefCell::new(None),
|
||||
sender: RefCell::new(None),
|
||||
hash: Cell::new(None),
|
||||
sender: Cell::new(None),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -238,13 +238,14 @@ impl SignedTransaction {
|
||||
|
||||
/// Get the hash of this header (sha3 of the RLP).
|
||||
pub fn hash(&self) -> H256 {
|
||||
let mut hash = self.hash.borrow_mut();
|
||||
match &mut *hash {
|
||||
&mut Some(ref h) => h.clone(),
|
||||
hash @ &mut None => {
|
||||
*hash = Some(self.rlp_sha3());
|
||||
hash.as_ref().unwrap().clone()
|
||||
}
|
||||
let hash = self.hash.get();
|
||||
match hash {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
let h = self.rlp_sha3();
|
||||
self.hash.set(Some(h));
|
||||
h
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,13 +266,14 @@ impl SignedTransaction {
|
||||
|
||||
/// Returns transaction sender.
|
||||
pub fn sender(&self) -> Result<Address, Error> {
|
||||
let mut sender = self.sender.borrow_mut();
|
||||
match &mut *sender {
|
||||
&mut Some(ref h) => Ok(h.clone()),
|
||||
sender @ &mut None => {
|
||||
*sender = Some(From::from(try!(ec::recover(&self.signature(), &self.unsigned.hash())).sha3()));
|
||||
Ok(sender.as_ref().unwrap().clone())
|
||||
}
|
||||
let sender = self.sender.get();
|
||||
match sender {
|
||||
Some(s) => Ok(s),
|
||||
None => {
|
||||
let s = Address::from(try!(ec::recover(&self.signature(), &self.unsigned.hash())).sha3());
|
||||
self.sender.set(Some(s));
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user