Merge branch 'master' into sync-svc
This commit is contained in:
@@ -35,6 +35,8 @@ pub struct Account {
|
||||
code_hash: Option<H256>,
|
||||
// Code cache of the account.
|
||||
code_cache: Bytes,
|
||||
// Account is new or has been modified
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
@@ -47,7 +49,8 @@ impl Account {
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
storage_overlay: RefCell::new(storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
||||
code_hash: Some(code.sha3()),
|
||||
code_cache: code
|
||||
code_cache: code,
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +62,8 @@ impl Account {
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
||||
code_hash: Some(pod.code.sha3()),
|
||||
code_cache: pod.code
|
||||
code_cache: pod.code,
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +76,7 @@ impl Account {
|
||||
storage_overlay: RefCell::new(HashMap::new()),
|
||||
code_hash: Some(SHA3_EMPTY),
|
||||
code_cache: vec![],
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +90,7 @@ impl Account {
|
||||
storage_overlay: RefCell::new(HashMap::new()),
|
||||
code_hash: Some(r.val_at(3)),
|
||||
code_cache: vec![],
|
||||
dirty: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +104,7 @@ impl Account {
|
||||
storage_overlay: RefCell::new(HashMap::new()),
|
||||
code_hash: None,
|
||||
code_cache: vec![],
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +113,7 @@ impl Account {
|
||||
pub fn init_code(&mut self, code: Bytes) {
|
||||
assert!(self.code_hash.is_none());
|
||||
self.code_cache = code;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Reset this account's code to the given code.
|
||||
@@ -117,6 +125,7 @@ impl Account {
|
||||
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
|
||||
pub fn set_storage(&mut self, key: H256, value: H256) {
|
||||
self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value));
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Get (and cache) the contents of the trie's storage at `key`.
|
||||
@@ -172,6 +181,10 @@ impl Account {
|
||||
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == Some(SHA3_EMPTY))
|
||||
}
|
||||
|
||||
/// Is this a new or modified account?
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
self.dirty
|
||||
}
|
||||
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
|
||||
pub fn cache_code(&mut self, db: &AccountDB) -> bool {
|
||||
// TODO: fill out self.code_cache;
|
||||
@@ -201,16 +214,23 @@ impl Account {
|
||||
pub fn storage_overlay(&self) -> Ref<HashMap<H256, (Filth, H256)>> { self.storage_overlay.borrow() }
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); }
|
||||
pub fn inc_nonce(&mut self) {
|
||||
self.nonce = self.nonce + U256::from(1u8);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
pub fn add_balance(&mut self, x: &U256) { self.balance = self.balance + *x; }
|
||||
pub fn add_balance(&mut self, x: &U256) {
|
||||
self.balance = self.balance + *x;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
/// Panics if balance is less than `x`
|
||||
pub fn sub_balance(&mut self, x: &U256) {
|
||||
assert!(self.balance >= *x);
|
||||
self.balance = self.balance - *x;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
|
||||
|
||||
@@ -296,32 +296,20 @@ impl BlockChain {
|
||||
// load best block
|
||||
let best_block_hash = match bc.extras_db.get(b"best").unwrap() {
|
||||
Some(best) => {
|
||||
let best = H256::from_slice(&best);
|
||||
let mut b = best.clone();
|
||||
let mut removed = 0;
|
||||
let mut best_num = 0;
|
||||
while !bc.blocks_db.get(&b).unwrap().is_some() {
|
||||
// track back to the best block we have in the blocks database
|
||||
let extras: BlockDetails = bc.extras_db.read(&b).unwrap();
|
||||
type DetailsKey = Key<BlockDetails, Target=H264>;
|
||||
bc.extras_db.delete(&(DetailsKey::key(&b))).unwrap();
|
||||
b = extras.parent;
|
||||
best_num = extras.number;
|
||||
removed += 1;
|
||||
}
|
||||
if b != best {
|
||||
let batch = DBTransaction::new();
|
||||
let range = (best_num + 1) as bc::Number .. (best_num + removed) as bc::Number;
|
||||
let chain = bc::group::BloomGroupChain::new(bc.blooms_config, &bc);
|
||||
let changes = chain.replace(&range, vec![]);
|
||||
for (k, v) in changes.into_iter() {
|
||||
batch.write(&LogGroupPosition::from(k), &BloomGroup::from(v));
|
||||
let mut new_best = H256::from_slice(&best);
|
||||
while !bc.blocks_db.get(&new_best).unwrap().is_some() {
|
||||
match bc.rewind() {
|
||||
Some(h) => {
|
||||
new_best = h;
|
||||
}
|
||||
None => {
|
||||
warn!("Can't rewind blockchain");
|
||||
break;
|
||||
}
|
||||
}
|
||||
batch.put(b"best", &b).unwrap();
|
||||
bc.extras_db.write(batch).unwrap();
|
||||
info!("Restored mismatched best block. Was: {}, new: {}", best.hex(), b.hex());
|
||||
info!("Restored mismatched best block. Was: {}, new: {}", H256::from_slice(&best).hex(), new_best.hex());
|
||||
}
|
||||
b
|
||||
new_best
|
||||
}
|
||||
None => {
|
||||
// best block does not exist
|
||||
@@ -359,6 +347,52 @@ impl BlockChain {
|
||||
bc
|
||||
}
|
||||
|
||||
/// Returns true if the given parent block has given child
|
||||
/// (though not necessarily a part of the canon chain).
|
||||
fn is_known_child(&self, parent: &H256, hash: &H256) -> bool {
|
||||
self.extras_db.read_with_cache(&self.block_details, parent).map_or(false, |d| d.children.contains(hash))
|
||||
}
|
||||
|
||||
/// Rewind to a previous block
|
||||
pub fn rewind(&self) -> Option<H256> {
|
||||
let batch = DBTransaction::new();
|
||||
// track back to the best block we have in the blocks database
|
||||
if let Some(best_block_hash) = self.extras_db.get(b"best").unwrap() {
|
||||
let best_block_hash = H256::from_slice(&best_block_hash);
|
||||
if best_block_hash == self.genesis_hash() {
|
||||
return None;
|
||||
}
|
||||
if let Some(extras) = self.extras_db.read(&best_block_hash) as Option<BlockDetails> {
|
||||
type DetailsKey = Key<BlockDetails, Target=H264>;
|
||||
batch.delete(&(DetailsKey::key(&best_block_hash))).unwrap();
|
||||
let hash = extras.parent;
|
||||
let range = extras.number as bc::Number .. extras.number as bc::Number;
|
||||
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
|
||||
let changes = chain.replace(&range, vec![]);
|
||||
for (k, v) in changes.into_iter() {
|
||||
batch.write(&LogGroupPosition::from(k), &BloomGroup::from(v));
|
||||
}
|
||||
batch.put(b"best", &hash).unwrap();
|
||||
let mut best_block = self.best_block.write();
|
||||
best_block.number = extras.number - 1;
|
||||
best_block.total_difficulty = self.block_details(&hash).unwrap().total_difficulty;
|
||||
best_block.hash = hash;
|
||||
// update parent extras
|
||||
if let Some(mut details) = self.extras_db.read(&hash) as Option<BlockDetails> {
|
||||
details.children.clear();
|
||||
batch.write(&hash, &details);
|
||||
}
|
||||
self.extras_db.write(batch).unwrap();
|
||||
self.block_details.write().clear();
|
||||
self.block_hashes.write().clear();
|
||||
self.blocks.write().clear();
|
||||
self.block_receipts.write().clear();
|
||||
return Some(hash);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Set the cache configuration.
|
||||
pub fn configure_cache(&self, pref_cache_size: usize, max_cache_size: usize) {
|
||||
self.pref_cache_size.store(pref_cache_size, AtomicOrder::Relaxed);
|
||||
@@ -463,7 +497,7 @@ impl BlockChain {
|
||||
let header = block.header_view();
|
||||
let hash = header.sha3();
|
||||
|
||||
if self.is_known(&hash) {
|
||||
if self.is_known_child(&header.parent_hash(), &hash) {
|
||||
return ImportRoute::none();
|
||||
}
|
||||
|
||||
@@ -508,14 +542,15 @@ impl BlockChain {
|
||||
batch.extend_with_cache(&mut *write_blocks_blooms, update.blocks_blooms, CacheUpdatePolicy::Remove);
|
||||
}
|
||||
|
||||
// These cached values must be updated last and togeterh
|
||||
// These cached values must be updated last with all three locks taken to avoid
|
||||
// cache decoherence
|
||||
{
|
||||
let mut best_block = self.best_block.write();
|
||||
// update best block
|
||||
match update.info.location {
|
||||
BlockLocation::Branch => (),
|
||||
_ => {
|
||||
batch.put(b"best", &update.info.hash).unwrap();
|
||||
let mut best_block = self.best_block.write();
|
||||
*best_block = BestBlock {
|
||||
hash: update.info.hash,
|
||||
number: update.info.number,
|
||||
@@ -1216,4 +1251,30 @@ mod tests {
|
||||
let bc = BlockChain::new(Config::default(), &genesis, temp.as_path());
|
||||
assert_eq!(bc.best_block_number(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rewind() {
|
||||
let mut canon_chain = ChainGenerator::default();
|
||||
let mut finalizer = BlockFinalizer::default();
|
||||
let genesis = canon_chain.generate(&mut finalizer).unwrap();
|
||||
let first = canon_chain.generate(&mut finalizer).unwrap();
|
||||
let second = canon_chain.generate(&mut finalizer).unwrap();
|
||||
let genesis_hash = BlockView::new(&genesis).header_view().sha3();
|
||||
let first_hash = BlockView::new(&first).header_view().sha3();
|
||||
let second_hash = BlockView::new(&second).header_view().sha3();
|
||||
|
||||
let temp = RandomTempPath::new();
|
||||
let bc = BlockChain::new(Config::default(), &genesis, temp.as_path());
|
||||
|
||||
bc.insert_block(&first, vec![]);
|
||||
bc.insert_block(&second, vec![]);
|
||||
|
||||
assert_eq!(bc.rewind(), Some(first_hash.clone()));
|
||||
assert!(!bc.is_known(&second_hash));
|
||||
assert_eq!(bc.best_block_number(), 1);
|
||||
assert_eq!(bc.best_block_hash(), first_hash.clone());
|
||||
|
||||
assert_eq!(bc.rewind(), Some(genesis_hash.clone()));
|
||||
assert_eq!(bc.rewind(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,21 +22,18 @@ use std::sync::{Arc, Weak};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fmt;
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::time::Instant;
|
||||
use std::time::{Instant, Duration};
|
||||
use time::precise_time_ns;
|
||||
|
||||
// util
|
||||
use util::{journaldb, rlp, Bytes, Stream, View, PerfTimer, Itertools, Mutex, RwLock, Colour};
|
||||
use util::journaldb::JournalDB;
|
||||
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
|
||||
use util::numbers::*;
|
||||
use util::panics::*;
|
||||
use util::io::*;
|
||||
use util::rlp;
|
||||
use util::sha3::*;
|
||||
use util::Bytes;
|
||||
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
|
||||
use util::journaldb;
|
||||
use util::journaldb::JournalDB;
|
||||
use util::kvdb::*;
|
||||
use util::{Stream, View, PerfTimer, Itertools};
|
||||
use util::{Mutex, RwLock};
|
||||
|
||||
// other
|
||||
use views::BlockView;
|
||||
@@ -145,6 +142,9 @@ pub struct Client {
|
||||
notify: RwLock<Option<Weak<ChainNotify>>>,
|
||||
queue_transactions: AtomicUsize,
|
||||
previous_enode: Mutex<Option<String>>,
|
||||
skipped: AtomicUsize,
|
||||
last_import: Mutex<Instant>,
|
||||
last_hashes: RwLock<VecDeque<H256>>,
|
||||
}
|
||||
|
||||
const HISTORY: u64 = 1200;
|
||||
@@ -205,6 +205,11 @@ impl Client {
|
||||
state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
||||
}
|
||||
|
||||
while !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.contains(h.state_root())) {
|
||||
warn!("State root not found for block #{} ({}), recovering...", chain.best_block_number(), chain.best_block_hash().hex());
|
||||
chain.rewind();
|
||||
}
|
||||
|
||||
let engine = Arc::new(spec.engine);
|
||||
|
||||
let block_queue = BlockQueue::new(config.queue, engine.clone(), message_channel.clone());
|
||||
@@ -232,6 +237,9 @@ impl Client {
|
||||
notify: RwLock::new(None),
|
||||
queue_transactions: AtomicUsize::new(0),
|
||||
previous_enode: Mutex::new(None),
|
||||
skipped: AtomicUsize::new(0),
|
||||
last_import: Mutex::new(Instant::now()),
|
||||
last_hashes: RwLock::new(VecDeque::new()),
|
||||
};
|
||||
Ok(Arc::new(client))
|
||||
}
|
||||
@@ -253,6 +261,14 @@ impl Client {
|
||||
}
|
||||
|
||||
fn build_last_hashes(&self, parent_hash: H256) -> LastHashes {
|
||||
{
|
||||
let hashes = self.last_hashes.read();
|
||||
if hashes.front().map_or(false, |h| h == &parent_hash) {
|
||||
let mut res = Vec::from(hashes.clone());
|
||||
res.resize(256, H256::default());
|
||||
return res;
|
||||
}
|
||||
}
|
||||
let mut last_hashes = LastHashes::new();
|
||||
last_hashes.resize(256, H256::new());
|
||||
last_hashes[0] = parent_hash;
|
||||
@@ -264,6 +280,8 @@ impl Client {
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
let mut cached_hashes = self.last_hashes.write();
|
||||
*cached_hashes = VecDeque::from(last_hashes.clone());
|
||||
last_hashes
|
||||
}
|
||||
|
||||
@@ -355,16 +373,21 @@ impl Client {
|
||||
|
||||
for block in blocks {
|
||||
let header = &block.header;
|
||||
let start = precise_time_ns();
|
||||
|
||||
if invalid_blocks.contains(&header.parent_hash) {
|
||||
invalid_blocks.insert(header.hash());
|
||||
continue;
|
||||
}
|
||||
let tx_count = block.transactions.len();
|
||||
let size = block.bytes.len();
|
||||
|
||||
let closed_block = self.check_and_close_block(&block);
|
||||
if let Err(_) = closed_block {
|
||||
invalid_blocks.insert(header.hash());
|
||||
continue;
|
||||
}
|
||||
|
||||
let closed_block = closed_block.unwrap();
|
||||
imported_blocks.push(header.hash());
|
||||
|
||||
@@ -372,7 +395,30 @@ impl Client {
|
||||
import_results.push(route);
|
||||
|
||||
self.report.write().accrue_block(&block);
|
||||
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
||||
|
||||
let duration_ns = precise_time_ns() - start;
|
||||
|
||||
let mut last_import = self.last_import.lock();
|
||||
if Instant::now() > *last_import + Duration::from_secs(1) {
|
||||
let queue_info = self.queue_info();
|
||||
let importing = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3;
|
||||
if !importing {
|
||||
let skipped = self.skipped.load(AtomicOrdering::Relaxed);
|
||||
info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}",
|
||||
Colour::White.bold().paint(format!("#{}", header.number())),
|
||||
Colour::White.bold().paint(format!("{}", header.hash())),
|
||||
Colour::Yellow.bold().paint(format!("{}", tx_count)),
|
||||
Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used.low_u64() as f32 / 1000000f32)),
|
||||
Colour::Purple.bold().paint(format!("{:.2}", duration_ns as f32 / 1000000f32)),
|
||||
Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)),
|
||||
if skipped > 0 { format!(" + another {} block(s)", Colour::Red.bold().paint(format!("{}", skipped))) } else { String::new() }
|
||||
);
|
||||
*last_import = Instant::now();
|
||||
}
|
||||
self.skipped.store(0, AtomicOrdering::Relaxed);
|
||||
} else {
|
||||
self.skipped.fetch_add(1, AtomicOrdering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
let imported = imported_blocks.len();
|
||||
@@ -418,6 +464,7 @@ impl Client {
|
||||
|
||||
fn commit_block<B>(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain {
|
||||
let number = block.header().number();
|
||||
let parent = block.header().parent_hash().clone();
|
||||
// Are we committing an era?
|
||||
let ancient = if number >= HISTORY {
|
||||
let n = number - HISTORY;
|
||||
@@ -445,9 +492,20 @@ impl Client {
|
||||
enacted: route.enacted.clone(),
|
||||
retracted: route.retracted.len()
|
||||
});
|
||||
self.update_last_hashes(&parent, hash);
|
||||
route
|
||||
}
|
||||
|
||||
fn update_last_hashes(&self, parent: &H256, hash: &H256) {
|
||||
let mut hashes = self.last_hashes.write();
|
||||
if hashes.front().map_or(false, |h| h == parent) {
|
||||
if hashes.len() > 255 {
|
||||
hashes.pop_back();
|
||||
}
|
||||
hashes.push_front(hash.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Import transactions from the IO queue
|
||||
pub fn import_queued_transactions(&self, transactions: &[Bytes]) -> usize {
|
||||
let _timer = PerfTimer::new("import_queued_transactions");
|
||||
|
||||
@@ -59,8 +59,6 @@ pub enum TransactionError {
|
||||
},
|
||||
/// Transaction's gas limit (aka gas) is invalid.
|
||||
InvalidGasLimit(OutOfBounds<U256>),
|
||||
/// Transaction is invalid for some other reason.
|
||||
DAORescue,
|
||||
}
|
||||
|
||||
impl fmt::Display for TransactionError {
|
||||
@@ -79,7 +77,6 @@ impl fmt::Display for TransactionError {
|
||||
GasLimitExceeded { limit, got } =>
|
||||
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
|
||||
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
|
||||
DAORescue => "Transaction is invalid due to the DAO rescue.".into(),
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("Transaction error ({})", msg))
|
||||
|
||||
@@ -80,8 +80,6 @@ pub struct Schedule {
|
||||
pub tx_data_non_zero_gas: usize,
|
||||
/// Gas price for copying memory
|
||||
pub copy_gas: usize,
|
||||
/// DAO Rescue softfork block
|
||||
pub reject_dao_transactions: bool,
|
||||
}
|
||||
|
||||
impl Schedule {
|
||||
@@ -128,7 +126,6 @@ impl Schedule {
|
||||
tx_data_zero_gas: 4,
|
||||
tx_data_non_zero_gas: 68,
|
||||
copy_gas: 3,
|
||||
reject_dao_transactions: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,36 +232,10 @@ impl State {
|
||||
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
||||
let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options));
|
||||
|
||||
let broken_dao = H256::from("6a5d24750f78441e56fec050dc52fe8e911976485b7472faac7464a176a67caa");
|
||||
|
||||
// dao attack soft fork
|
||||
if engine.schedule(&env_info).reject_dao_transactions {
|
||||
let whitelisted = if let Action::Call(to) = t.action {
|
||||
to == Address::from("Da4a4626d3E16e094De3225A751aAb7128e96526") ||
|
||||
to == Address::from("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334")
|
||||
} else { false };
|
||||
if !whitelisted {
|
||||
// collect all the addresses which have changed.
|
||||
let addresses = self.cache.borrow().iter().map(|(addr, _)| addr.clone()).collect::<Vec<_>>();
|
||||
|
||||
for a in &addresses {
|
||||
if self.code(a).map_or(false, |c| c.sha3() == broken_dao) {
|
||||
// Figure out if the balance has been reduced.
|
||||
let maybe_original = self.trie_factory
|
||||
.readonly(self.db.as_hashdb(), &self.root)
|
||||
.expect(SEC_TRIE_DB_UNWRAP_STR)
|
||||
.get(&a).map(Account::from_rlp);
|
||||
if maybe_original.map_or(false, |original| *original.balance() > self.balance(a)) {
|
||||
return Err(Error::Transaction(TransactionError::DAORescue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO uncomment once to_pod() works correctly.
|
||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||
self.commit();
|
||||
self.clear();
|
||||
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
|
||||
// trace!("Transaction receipt: {:?}", receipt);
|
||||
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
||||
@@ -275,12 +249,12 @@ impl State {
|
||||
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
|
||||
for (address, ref mut a) in accounts.iter_mut() {
|
||||
match a {
|
||||
&mut&mut Some(ref mut account) => {
|
||||
&mut&mut Some(ref mut account) if account.is_dirty() => {
|
||||
let mut account_db = AccountDBMut::new(db, address);
|
||||
account.commit_storage(trie_factory, &mut account_db);
|
||||
account.commit_code(&mut account_db);
|
||||
}
|
||||
&mut&mut None => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,8 +262,9 @@ impl State {
|
||||
let mut trie = trie_factory.from_existing(db, root).unwrap();
|
||||
for (address, ref a) in accounts.iter() {
|
||||
match **a {
|
||||
Some(ref account) => trie.insert(address, &account.rlp()),
|
||||
Some(ref account) if account.is_dirty() => trie.insert(address, &account.rlp()),
|
||||
None => trie.remove(address),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,6 +276,11 @@ impl State {
|
||||
Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, self.cache.borrow_mut().deref_mut());
|
||||
}
|
||||
|
||||
/// Clear state cache
|
||||
pub fn clear(&mut self) {
|
||||
self.cache.borrow_mut().clear();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "json-tests")]
|
||||
/// Populate the state from `accounts`.
|
||||
|
||||
Reference in New Issue
Block a user