Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.

Commit 6c1b2fb

Browse files
arkpargavofyork
authored andcommitted
Preserve cache on reverting the snapshot (#2488)
* Preserve cache on reverting the snapshot * Renamed merge_with into replace_with * Renamed and documented snapshotting methods
1 parent e380955 commit 6c1b2fb

File tree

4 files changed

+63
-22
lines changed

4 files changed

+63
-22
lines changed

ethcore/src/executive.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ impl<'a> Executive<'a> {
265265
let cost = self.engine.cost_of_builtin(&params.code_address, data);
266266
if cost <= params.gas {
267267
self.engine.execute_builtin(&params.code_address, data, &mut output);
268-
self.state.clear_snapshot();
268+
self.state.discard_snapshot();
269269

270270
// trace only top level calls to builtins to avoid DDoS attacks
271271
if self.depth == 0 {
@@ -285,7 +285,7 @@ impl<'a> Executive<'a> {
285285
Ok(params.gas - cost)
286286
} else {
287287
// just drain the whole gas
288-
self.state.revert_snapshot();
288+
self.state.revert_to_snapshot();
289289

290290
tracer.trace_failed_call(trace_info, vec![], evm::Error::OutOfGas.into());
291291

@@ -331,7 +331,7 @@ impl<'a> Executive<'a> {
331331
res
332332
} else {
333333
// otherwise it's just a basic transaction, only do tracing, if necessary.
334-
self.state.clear_snapshot();
334+
self.state.discard_snapshot();
335335

336336
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
337337
Ok(params.gas)
@@ -473,10 +473,10 @@ impl<'a> Executive<'a> {
473473
| Err(evm::Error::BadInstruction {.. })
474474
| Err(evm::Error::StackUnderflow {..})
475475
| Err(evm::Error::OutOfStack {..}) => {
476-
self.state.revert_snapshot();
476+
self.state.revert_to_snapshot();
477477
},
478478
Ok(_) | Err(evm::Error::Internal) => {
479-
self.state.clear_snapshot();
479+
self.state.discard_snapshot();
480480
substate.accrue(un_substate);
481481
}
482482
}

ethcore/src/state/account.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ impl Account {
394394
/// Replace self with the data from other account merging storage cache.
395395
/// Basic account data and all modifications are overwritten
396396
/// with new values.
397-
pub fn merge_with(&mut self, other: Account) {
397+
pub fn overwrite_with(&mut self, other: Account) {
398398
self.balance = other.balance;
399399
self.nonce = other.nonce;
400400
self.storage_root = other.storage_root;

ethcore/src/state/mod.rs

+56-15
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,35 @@ impl AccountEntry {
9393
}
9494
}
9595

96-
// Create a new account entry and mark it as dirty
96+
// Create a new account entry and mark it as dirty.
9797
fn new_dirty(account: Option<Account>) -> AccountEntry {
9898
AccountEntry {
9999
account: account,
100100
state: AccountState::Dirty,
101101
}
102102
}
103-
104-
// Create a new account entry and mark it as clean
103+
104+
// Create a new account entry and mark it as clean.
105105
fn new_clean(account: Option<Account>) -> AccountEntry {
106106
AccountEntry {
107107
account: account,
108108
state: AccountState::Clean,
109109
}
110110
}
111+
112+
// Replace data with another entry but preserve storage cache.
113+
fn overwrite_with(&mut self, other: AccountEntry) {
114+
self.state = other.state;
115+
match other.account {
116+
Some(acc) => match self.account {
117+
Some(ref mut ours) => {
118+
ours.overwrite_with(acc);
119+
},
120+
None => {},
121+
},
122+
None => self.account = None,
123+
}
124+
}
111125
}
112126

113127
/// Representation of the entire state of all accounts in the system.
@@ -142,10 +156,24 @@ impl AccountEntry {
142156
///
143157
/// Upon destruction all the local cache data merged into the global cache.
144158
/// The merge might be rejected if current state is non-canonical.
159+
///
160+
/// State snapshotting.
161+
///
162+
/// A new snapshot can be created with `snapshot()`. Snapshots can be
163+
/// created in a hierarchy.
164+
/// When a snapshot is active all changes are applied directly into
165+
/// `cache` and the original value is copied into an active snapshot.
166+
/// Reverting a snapshot with `revert_to_snapshot` involves copying
167+
/// original values from the latest snapshot back into `cache`. The code
168+
/// takes care not to overwrite cached storage while doing that.
169+
/// Snapshot can be discateded with `discard_snapshot`. All of the orignal
170+
/// backed-up values are moved into a parent snapshot (if any).
171+
///
145172
pub struct State {
146173
db: StateDB,
147174
root: H256,
148175
cache: RefCell<HashMap<Address, AccountEntry>>,
176+
// The original account is preserved in
149177
snapshots: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
150178
account_start_nonce: U256,
151179
factories: Factories,
@@ -199,31 +227,44 @@ impl State {
199227
Ok(state)
200228
}
201229

202-
/// Create a recoverable snaphot of this state
230+
/// Create a recoverable snaphot of this state.
203231
pub fn snapshot(&mut self) {
204232
self.snapshots.borrow_mut().push(HashMap::new());
205233
}
206234

207-
/// Merge last snapshot with previous
208-
pub fn clear_snapshot(&mut self) {
235+
/// Merge last snapshot with previous.
236+
pub fn discard_snapshot(&mut self) {
209237
// merge with previous snapshot
210238
let last = self.snapshots.borrow_mut().pop();
211239
if let Some(mut snapshot) = last {
212240
if let Some(ref mut prev) = self.snapshots.borrow_mut().last_mut() {
213-
for (k, v) in snapshot.drain() {
214-
prev.entry(k).or_insert(v);
241+
if prev.is_empty() {
242+
**prev = snapshot;
243+
} else {
244+
for (k, v) in snapshot.drain() {
245+
prev.entry(k).or_insert(v);
246+
}
215247
}
216248
}
217249
}
218250
}
219251

220-
/// Revert to snapshot
221-
pub fn revert_snapshot(&mut self) {
252+
/// Revert to the last snapshot and discard it.
253+
pub fn revert_to_snapshot(&mut self) {
222254
if let Some(mut snapshot) = self.snapshots.borrow_mut().pop() {
223255
for (k, v) in snapshot.drain() {
224256
match v {
225257
Some(v) => {
226-
self.cache.borrow_mut().insert(k, v);
258+
match self.cache.borrow_mut().entry(k) {
259+
Entry::Occupied(mut e) => {
260+
// Merge snapshotted changes back into the main account
261+
// storage preserving the cache.
262+
e.get_mut().overwrite_with(v);
263+
},
264+
Entry::Vacant(e) => {
265+
e.insert(v);
266+
}
267+
}
227268
},
228269
None => {
229270
match self.cache.borrow_mut().entry(k) {
@@ -1717,12 +1758,12 @@ fn snapshot_basic() {
17171758
state.snapshot();
17181759
state.add_balance(&a, &U256::from(69u64));
17191760
assert_eq!(state.balance(&a), U256::from(69u64));
1720-
state.clear_snapshot();
1761+
state.discard_snapshot();
17211762
assert_eq!(state.balance(&a), U256::from(69u64));
17221763
state.snapshot();
17231764
state.add_balance(&a, &U256::from(1u64));
17241765
assert_eq!(state.balance(&a), U256::from(70u64));
1725-
state.revert_snapshot();
1766+
state.revert_to_snapshot();
17261767
assert_eq!(state.balance(&a), U256::from(69u64));
17271768
}
17281769

@@ -1735,9 +1776,9 @@ fn snapshot_nested() {
17351776
state.snapshot();
17361777
state.add_balance(&a, &U256::from(69u64));
17371778
assert_eq!(state.balance(&a), U256::from(69u64));
1738-
state.clear_snapshot();
1779+
state.discard_snapshot();
17391780
assert_eq!(state.balance(&a), U256::from(69u64));
1740-
state.revert_snapshot();
1781+
state.revert_to_snapshot();
17411782
assert_eq!(state.balance(&a), U256::from(0));
17421783
}
17431784

ethcore/src/state_db.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl StateDB {
191191
for (address, account) in self.cache_overlay.drain(..) {
192192
if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&address) {
193193
if let Some(new) = account {
194-
existing.merge_with(new);
194+
existing.overwrite_with(new);
195195
continue;
196196
}
197197
}

0 commit comments

Comments
 (0)