13
13
module to use an optimized database backed state.
14
14
"""
15
15
import logging
16
+ from collections import defaultdict
16
17
from dataclasses import dataclass
17
18
from importlib import import_module
18
19
from typing import Any , ClassVar , Dict , List , Optional , Set , cast
@@ -77,7 +78,7 @@ class State:
77
78
db : Any
78
79
dirty_accounts : Dict [Address , Optional [Account_ ]]
79
80
dirty_storage : Dict [Address , Dict [Bytes32 , U256 ]]
80
- destroyed_accounts : Set [Address ]
81
+ destroyed_accounts : Dict [Address , Uint ]
81
82
tx_restore_points : List [int ]
82
83
journal : List [Any ]
83
84
created_accounts : Set [Address ]
@@ -91,7 +92,7 @@ def __init__(self, path: Optional[str] = None) -> None:
91
92
self .db = rust_pyspec_glue .DB (path )
92
93
self .dirty_accounts = {}
93
94
self .dirty_storage = {}
94
- self .destroyed_accounts = set ( )
95
+ self .destroyed_accounts = defaultdict ( lambda : Uint ( 0 ) )
95
96
self .tx_restore_points = []
96
97
self .journal = []
97
98
self .created_accounts = set ()
@@ -184,14 +185,15 @@ def flush(state: State) -> None:
184
185
"""
185
186
if state .tx_restore_points :
186
187
raise Exception ("In a non-db transaction" )
187
- for address in state .destroyed_accounts :
188
- state .db .destroy_storage (address )
188
+ for address , count in state .destroyed_accounts .items ():
189
+ if count :
190
+ state .db .destroy_storage (address )
189
191
for address , account in state .dirty_accounts .items ():
190
192
state .db .set_account (address , account )
191
193
for address , storage in state .dirty_storage .items ():
192
194
for key , value in storage .items ():
193
195
state .db .set_storage (address , key , value )
194
- state .destroyed_accounts = set ()
196
+ state .destroyed_accounts . clear ()
195
197
state .dirty_accounts .clear ()
196
198
state .dirty_storage .clear ()
197
199
@@ -205,7 +207,7 @@ def rollback_db_transaction(state: State) -> None:
205
207
state .db .rollback_mutable ()
206
208
state .dirty_accounts .clear ()
207
209
state .dirty_storage .clear ()
208
- state .destroyed_accounts = set ()
210
+ state .destroyed_accounts . clear ()
209
211
210
212
def _begin_transaction (state : State ) -> None :
211
213
"""
@@ -291,8 +293,10 @@ def _rollback_transaction(state: State) -> None:
291
293
state .dirty_storage [item [0 ]][item [1 ]] = item [2 ]
292
294
elif type (item [1 ]) is dict :
293
295
# Restore storage that was destroyed by `destroy_storage()`
294
- state .destroyed_accounts .remove (item [0 ])
295
- state .dirty_storage [item [0 ]] = item [1 ]
296
+ state .destroyed_accounts [item [0 ]] -= Uint (1 )
297
+ if state .destroyed_accounts [item [0 ]] == 0 :
298
+ state .dirty_storage [item [0 ]] = item [1 ]
299
+ del state .destroyed_accounts [item [0 ]]
296
300
else :
297
301
# Revert a change to an account
298
302
if item [1 ] is Unmodified :
@@ -338,7 +342,7 @@ def get_storage(state: State, address: Address, key: Bytes32) -> U256:
338
342
):
339
343
return state .dirty_storage [address ][key ]
340
344
341
- if address in state .destroyed_accounts :
345
+ if state .destroyed_accounts [ address ] :
342
346
return U256 (0 )
343
347
else :
344
348
return U256 (state .db .get_storage (address , key ))
@@ -410,7 +414,7 @@ def destroy_storage(state: State, address: Address) -> None:
410
414
See `state`.
411
415
"""
412
416
state .journal .append ((address , state .dirty_storage .pop (address , {})))
413
- state .destroyed_accounts . add ( address )
417
+ state .destroyed_accounts [ address ] += Uint ( 1 )
414
418
set_account (state , address , get_account_optional (state , address ))
415
419
416
420
@add_item (patches )
@@ -430,7 +434,7 @@ def account_has_storage(state: State, address: Address) -> bool:
430
434
if v != U256 (0 ):
431
435
return True
432
436
433
- if address in state .destroyed_accounts :
437
+ if state .destroyed_accounts [ address ] :
434
438
return False
435
439
436
440
return state .db .has_storage (address )
0 commit comments