Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@ license = "GPL-3.0"
authors = ["Open Ethereum developers", "Parity Technologies <admin@parity.io>"]

[dependencies]
account-state = {path = "ethcore/account-state" }
ansi_term = "0.11"
atty = "0.2.8"
blooms-db = { path = "util/blooms-db" }
clap = "2"
cli-signer= { path = "cli-signer" }
client-traits = { path = "ethcore/client-traits" }
common-types = { path = "ethcore/types" }
crossbeam-utils = "0.7.2"
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
dir = { path = "util/dir" }
docopt = "1.0"
engine = { path = "ethcore/engine" }
ethabi = { version = "9.0.1", optional = true }
ethcore = { path = "ethcore", features = ["parity"] }
ethcore-accounts = { path = "accounts", optional = true }
ethcore-bloom-journal = { path = "util/bloom" }
ethcore-blockchain = { path = "ethcore/blockchain" }
ethcore-call-contract = { path = "ethcore/call-contract", optional = true }
ethcore-db = { path = "ethcore/db" }
Expand All @@ -45,7 +48,7 @@ kvdb-rocksdb = "0.6.0"
log = "0.4"
migration-rocksdb = { path = "util/migration-rocksdb" }
node-filter = { path = "ethcore/node-filter" }
num_cpus = "1.2"
num_cpus = "1.12"
number_prefix = "0.2"
panic_hook = { path = "util/panic-hook" }
parity-bytes = "0.1"
Expand All @@ -61,6 +64,7 @@ parity-updater = { path = "updater" }
parity-util-mem = { version = "0.5.1", features = ["jemalloc-global"] }
parity-version = { path = "util/version" }
parking_lot = "0.10.0"
patricia-trie-ethereum = { path = "util/patricia-trie-ethereum" }
regex = "1.0"
registrar = { path = "util/registrar" }
rlp = "0.4.0"
Expand All @@ -72,9 +76,11 @@ serde_derive = "1.0"
serde_json = "1.0"
snapshot = { path = "ethcore/snapshot" }
spec = { path = "ethcore/spec" }
state-db = { path = "ethcore/state-db" }
term_size = "0.3"
textwrap = "0.9"
toml = "0.5.6"
trie-db = "0.20.0"
verification = { path = "ethcore/verification" }

[build-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion accounts/ethstore/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
docopt = "1.0"
env_logger = "0.5"
num_cpus = "1.6"
num_cpus = "1.12"
rustc-hex = "2.1.0"
serde = "1.0"
serde_derive = "1.0"
Expand Down
132 changes: 91 additions & 41 deletions ethcore/state-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use ethereum_types::{Address, H256};
use hash_db::HashDB;
use keccak_hash::keccak;
use kvdb::{DBTransaction, DBValue, KeyValueDB};
use log::trace;
use log::{debug, trace, warn};
use lru_cache::LruCache;
use parking_lot::Mutex;

Expand All @@ -36,24 +36,25 @@ use journaldb::JournalDB;
use keccak_hasher::KeccakHasher;
use memory_cache::MemoryLruCache;

/// Value used to initialize bloom bitmap size.
///
/// Bitmap size is the size in bytes (not bits) that will be allocated in memory.
pub const ACCOUNT_BLOOM_SPACE: usize = 1048576;

/// Value used to initialize bloom items count.
/// Value used to initialize the bloom items count for new DBs
///
/// Items count is an estimation of the maximum number of items to store.
pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000;

/// Key for a value storing amount of hashes
pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
// todo[dvdplm] Determine the best value here. Should probably be twice as big.
pub const ACCOUNTS_BLOOM_ITEM_COUNT: u64 = 100_000_000;
/// False positive rate for the accounts bloom filter: 1 in 100.
pub const ACCOUNTS_BLOOM_FP_RATE: f64 = 0.01;
/// Key storing the number of hash functions used in the accounts bloom.
pub const ACCOUNTS_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count";
/// Key storing number of items the accounts bloom was built to contain.
pub const ACCOUNTS_BLOOM_ITEM_COUNT_KEY: &'static [u8] = b"accounts_bloom_item_count";

const STATE_CACHE_BLOCKS: usize = 12;

// The percentage of supplied cache size to go to accounts.
const ACCOUNT_CACHE_RATIO: usize = 90;

const DB_ERROR: &'static str = "Low-level database error";

/// Shared canonical state cache.
struct AccountCache {
/// DB Account cache. `None` indicates that account is known to be missing.
Expand Down Expand Up @@ -159,43 +160,92 @@ impl StateDB {
}
}

/// Loads accounts bloom from the database
/// This bloom is used to handle request for the non-existent account fast
pub fn load_bloom(db: &dyn KeyValueDB) -> Bloom {
let hash_count_entry = db.get(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY)
.expect("Low-level database error");

let hash_count_bytes = match hash_count_entry {
Some(bytes) => bytes,
None => return Bloom::new(ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET),
};

assert_eq!(hash_count_bytes.len(), 1);
let hash_count = hash_count_bytes[0];

let mut bloom_parts = vec![0u64; ACCOUNT_BLOOM_SPACE / 8];
for i in 0..ACCOUNT_BLOOM_SPACE / 8 {
let key: [u8; 8] = (i as u64).to_le_bytes();
bloom_parts[i] = db.get(COL_ACCOUNT_BLOOM, &key).expect("low-level database error")
.map(|val| {
assert_eq!(val.len(), 8, "low-level database error");
fn fetch_bloom_parts(db: &dyn KeyValueDB, bitmap_size: u64) -> Vec<u64> {
let nr_parts = bitmap_size / 8;
let mut bloom_parts = vec![0u64; nr_parts as usize];
trace!(target: "accounts_bloom]", "Fething bloom from disk. bitmap_size={}, nr_parts={}", bitmap_size, nr_parts);

let start = std::time::Instant::now();
for (k, v) in db.iter(COL_ACCOUNT_BLOOM) {
// The only keys in the `COL_ACCOUNT_BLOOM` that are not `u64`s are
// the two keys where we store the number of hash functions for
// legacy blooms (ACCOUNTS_BLOOM_HASHCOUNT_KEY) and the number of
// estimated items for the bloom (ACCOUNTS_BLOOM_ITEM_COUNT_KEY).
if k.len() == 8 {
let part_idx = {
let mut buff = [0u8; 8];
buff.copy_from_slice(&*val);
buff.copy_from_slice(&*k);
u64::from_le_bytes(buff)
})
.unwrap_or(0u64);
};
if part_idx >= nr_parts {
warn!(target: "accounts_bloom", "Accounts bloom DB has a key out of bounds: {}/{:?}. Expected {} bloom parts.", part_idx, k, nr_parts);
} else {
bloom_parts[part_idx as usize] = {
let mut buff = [0u8; 8];
buff.copy_from_slice(&*v);
u64::from_le_bytes(buff)
};
}
} else {
assert!(
&*k == ACCOUNTS_BLOOM_HASHCOUNT_KEY || &*k == ACCOUNTS_BLOOM_ITEM_COUNT_KEY,
"Expect the DB to contain `u64`s or the above two keys – corrupt db?"
)
}
}
debug!(target: "accounts_bloom", "Fetched the bloom from the DB in {:?}. bloom_parts.len={}", start.elapsed(), bloom_parts.len());
bloom_parts
}

/// Loads accounts bloom from the database
/// This bloom is used to quickly handle requests for non-existent accounts.
pub fn load_bloom(db: &dyn KeyValueDB) -> Bloom {
let (bloom, item_count) =
if db.get(COL_ACCOUNT_BLOOM, ACCOUNTS_BLOOM_HASHCOUNT_KEY)
.expect(DB_ERROR)
.is_some() {
// The legacy values for bitmap size and hash function count
// (ACCOUNT_BLOOM_SPACE, DEFAULT_ACCOUNT_PRESET) are not
// optimal, so we can't calculate them.
let parts = Self::fetch_bloom_parts(db, 1048576);
(Bloom::from_parts_legacy(parts, 6), 1_000_000)
} else {
let item_count =
db.get(COL_ACCOUNT_BLOOM, ACCOUNTS_BLOOM_ITEM_COUNT_KEY)
.expect(DB_ERROR)
.and_then(|bytes| {
assert_eq!(bytes.len(), 8, "Expected a u64");
let mut buf = [0u8; 8];
buf.copy_from_slice(&*bytes);
let val = u64::from_le_bytes(buf);
trace!(target: "accounts_bloom", "DB has a value under 'accounts_bloom_item_count': {}", val);
Some(val)
})
// Assume this is a new DB
.unwrap_or_else(|| {
trace!(target: "accounts_bloom", "New database, building default bloom with space for {} accounts", ACCOUNTS_BLOOM_ITEM_COUNT);
let mut tx = DBTransaction::new();
tx.put(COL_ACCOUNT_BLOOM, ACCOUNTS_BLOOM_ITEM_COUNT_KEY, &ACCOUNTS_BLOOM_ITEM_COUNT.to_le_bytes());
db.write(tx).expect(DB_ERROR);
ACCOUNTS_BLOOM_ITEM_COUNT
});
let bitmap_size = Bloom::compute_bitmap_size(item_count, ACCOUNTS_BLOOM_FP_RATE);
let parts = Self::fetch_bloom_parts(db, bitmap_size);
(Bloom::from_parts(parts, item_count), item_count)
};

debug!(target: "accounts_bloom", "Bloom saturation: {:?}, hash functions: {:?}, bitmap size: {} bits",
bloom.saturation(), bloom.number_of_hash_functions(), bloom.number_of_bits());
if bloom.saturation() > 0.9 {
warn!("Your accounts bloom is almost full ({}). Please rebuild it with more space. Your current filter uses {} bits and was built for {} accounts.",
bloom.saturation(), bloom.number_of_bits(), item_count);
}

let bloom = Bloom::from_parts(&bloom_parts, hash_count as u32);
trace!(target: "account_bloom", "Bloom is {:?} full, hash functions count = {:?}", bloom.saturation(), hash_count);
bloom
}

/// Commit blooms journal to the database transaction
pub fn commit_bloom(batch: &mut DBTransaction, journal: BloomJournal) -> io::Result<()> {
assert!(journal.hash_functions <= 255);
batch.put(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &[journal.hash_functions as u8]);

for (bloom_part_index, bloom_part_value) in journal.entries {
let key: [u8; 8] = (bloom_part_index as u64).to_le_bytes();
let val: [u8; 8] = bloom_part_value.to_le_bytes();
Expand Down Expand Up @@ -463,13 +513,13 @@ impl account_state::Backend for StateDB {
}

fn note_non_null_account(&self, address: &Address) {
trace!(target: "account_bloom", "Note account bloom: {:?}", address);
trace!(target: "accounts_bloom", "Note account bloom: {:?}", address);
let mut bloom = self.account_bloom.lock();
bloom.set(keccak(address).as_bytes());
}

fn is_known_null(&self, address: &Address) -> bool {
trace!(target: "account_bloom", "Check account bloom: {:?}", address);
trace!(target: "accounts_bloom", "Check account bloom: {:?}", address);
let bloom = self.account_bloom.lock();
let is_null = !bloom.check(keccak(address).as_bytes());
is_null
Expand Down
Loading