Skip to content
This repository was archived by the owner on May 23, 2023. It is now read-only.

Commit fdfcf69

Browse files
committed
Added partial state cache
1 parent ecb14c9 commit fdfcf69

File tree

3 files changed

+60
-15
lines changed

3 files changed

+60
-15
lines changed

ethereum/genesis_helpers.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def block_from_genesis_declaration(genesis_data, env):
2121
gas_limit=parse_as_int(genesis_data["gasLimit"]))
2222
return Block(h, [], [])
2323

24-
def state_from_genesis_declaration(genesis_data, env, block=None, allow_empties=False):
24+
def state_from_genesis_declaration(genesis_data, env, block=None, allow_empties=False, executing_on_head=False):
2525
if block:
2626
assert isinstance(block, Block)
2727
else:
@@ -43,12 +43,15 @@ def state_from_genesis_declaration(genesis_data, env, block=None, allow_empties=
4343
for k, v in data['storage'].items():
4444
state.set_storage_data(addr, big_endian_to_int(parse_as_bin(k)), big_endian_to_int(parse_as_bin(v)))
4545
get_consensus_strategy(state.config).initialize(state, block)
46+
if executing_on_head:
47+
state.executing_on_head = True
4648
state.commit(allow_empties=allow_empties)
4749
print('deleting %d' % len(state.deletes))
4850
rdb = RefcountDB(state.db)
4951
for delete in state.deletes:
5052
rdb.delete(delete)
5153
block.header.state_root = state.trie.root_hash
54+
state.changed = {}
5255
state.prev_headers=[block.header]
5356
return state
5457

@@ -103,8 +106,8 @@ def mk_genesis_block(env, **kwargs):
103106
return block
104107

105108

106-
def mk_basic_state(alloc, header=None, env=None):
107-
state = State(env=env or Env())
109+
def mk_basic_state(alloc, header=None, env=None, executing_on_head=False):
110+
state = State(env=env or Env(), executing_on_head=executing_on_head)
108111
if not header:
109112
header = {
110113
"number": 0, "gas_limit": 4712388, "gas_used": 0,

ethereum/pow/chain.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def __init__(self, genesis=None, env=None, \
3535
# Initialize the state
3636
if 'head_hash' in self.db: # new head tag
3737
self.state = self.mk_poststate_of_blockhash(self.db.get('head_hash'))
38+
self.state.executing_on_head = True
3839
print('Initializing chain from saved head, #%d (%s)' % \
3940
(self.state.prev_headers[0].number, encode_hex(self.state.prev_headers[0].hash)))
4041
elif genesis is None:
@@ -47,11 +48,11 @@ def __init__(self, genesis=None, env=None, \
4748
reset_genesis = True
4849
elif "extraData" in genesis:
4950
self.state = state_from_genesis_declaration(
50-
genesis, self.env)
51+
genesis, self.env, executing_on_head=True)
5152
reset_genesis = True
5253
print('Initializing chain from provided genesis declaration')
5354
elif "prev_headers" in genesis:
54-
self.state = State.from_snapshot(genesis, self.env)
55+
self.state = State.from_snapshot(genesis, self.env, executing_on_head=True)
5556
reset_genesis = True
5657
print('Initializing chain from provided state snapshot, %d (%s)' % \
5758
(self.state.block_number, encode_hex(self.state.prev_headers[0].hash[:8])))
@@ -248,9 +249,10 @@ def add_block(self, block):
248249
(now, block.header.timestamp, block.header.timestamp - now))
249250
return False
250251
# Is the block being added to the head?
251-
self.state.deletes = []
252252
if block.header.prevhash == self.head_hash:
253253
log.info('Adding to head', head=encode_hex(block.header.prevhash))
254+
self.state.deletes = []
255+
self.state.changed = {}
254256
try:
255257
apply_block(self.state, block)
256258
except (AssertionError, KeyError, ValueError, InvalidTransaction, VerificationFailed) as e:
@@ -264,10 +266,11 @@ def add_block(self, block):
264266
self.db.put(b'txindex:' + tx.hash, rlp.encode([block.number, i]))
265267
assert self.get_blockhash_by_number(block.header.number) == block.header.hash
266268
deletes = self.state.deletes
269+
changed = self.state.changed
267270
# Or is the block being added to a chain that is not currently the head?
268271
elif block.header.prevhash in self.env.db:
269-
log.info('Receiving block not on head, adding to secondary post state',
270-
prevhash=encode_hex(block.header.prevhash))
272+
log.info('Receiving block not on head (%s), adding to secondary post state %s' %
273+
(encode_hex(self.head_hash), encode_hex(block.header.prevhash)))
271274
temp_state = self.mk_poststate_of_blockhash(block.header.prevhash)
272275
try:
273276
apply_block(temp_state, block)
@@ -277,6 +280,7 @@ def add_block(self, block):
277280
return False
278281
deletes = temp_state.deletes
279282
block_score = self.get_score(block)
283+
changed = temp_state.changed
280284
# If the block should be the new head, replace the head
281285
if block_score > self.get_score(self.head):
282286
b = block
@@ -291,7 +295,8 @@ def add_block(self, block):
291295
if b.prevhash not in self.db or self.db.get(b.prevhash) == 'GENESIS':
292296
break
293297
b = self.get_parent(b)
294-
# Replace block index and tx indices
298+
# Replace block index and tx indices, and edit the state cache
299+
changed_accts = {}
295300
replace_from = b.header.number
296301
for i in itertools.count(replace_from):
297302
log.info('Rewriting height %d' % i)
@@ -300,19 +305,40 @@ def add_block(self, block):
300305
if orig_at_height:
301306
self.db.delete(key)
302307
orig_block_at_height = self.get_block(orig_at_height)
308+
log.info('%s no longer in main chain' % encode_hex(orig_block_at_height.header.hash))
303309
for tx in orig_block_at_height.transactions:
304310
if b'txindex:' + tx.hash in self.db:
305311
self.db.delete(b'txindex:' + tx.hash)
312+
acct_list = self.db.get(b'changed:'+orig_block_at_height.hash)
313+
for j in range(0, len(acct_list), 20):
314+
changed_accts[acct_list[j: j+20]] = True
306315
if i in new_chain:
307316
new_block_at_height = new_chain[i]
317+
log.info('%s now in main chain' % encode_hex(new_block_at_height.header.hash))
308318
self.db.put(key, new_block_at_height.header.hash)
309319
for i, tx in enumerate(new_block_at_height.transactions):
310320
self.db.put(b'txindex:' + tx.hash,
311321
rlp.encode([new_block_at_height.number, i]))
322+
if i < b.number:
323+
acct_list = self.db.get(b'changed:'+new_block_at_height.hash)
324+
for j in range(0, len(acct_list), 20):
325+
changed_accts[acct_list[j: j+20]] = True
312326
if i not in new_chain and not orig_at_height:
313327
break
328+
for c in changed.keys():
329+
changed_accts[c] = True
330+
for addr in changed_accts.keys():
331+
data = temp_state.trie.get(addr)
332+
if data:
333+
self.state.db.put(b'address:'+addr, data)
334+
else:
335+
try:
336+
self.state.db.delete(b'address:'+addr)
337+
except KeyError:
338+
pass
314339
self.head_hash = block.header.hash
315340
self.state = temp_state
341+
self.state.executing_on_head = True
316342
# Block has no parent yet
317343
else:
318344
if block.header.prevhash not in self.parent_queue:
@@ -324,6 +350,8 @@ def add_block(self, block):
324350
self.add_child(block)
325351
self.db.put('head_hash', self.head_hash)
326352
self.db.put(block.hash, rlp.encode(block))
353+
self.db.put(b'changed:'+block.hash, b''.join(list(changed.keys())))
354+
print('Saved %d address change logs' % len(changed.keys()))
327355
self.db.put(b'deletes:'+block.hash, b''.join(deletes))
328356
print('Saved %d trie node deletes for block %d (%s)' % (len(deletes), block.number, utils.encode_hex(block.hash)))
329357
# Delete old junk data
@@ -336,6 +364,7 @@ def add_block(self, block):
336364
for i in range(0, len(deletes), 32):
337365
rdb.delete(deletes[i: i+32])
338366
self.db.delete(b'deletes:'+old_block_hash)
367+
self.db.delete(b'changed:'+old_block_hash)
339368
except KeyError as e:
340369
print(e)
341370
pass

ethereum/state.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def to_dict(self):
128128
#from ethereum.state import State
129129
class State():
130130

131-
def __init__(self, root=b'', env=Env(), **kwargs):
131+
def __init__(self, root=b'', env=Env(), executing_on_head=False, **kwargs):
132132
self.env = env
133133
self.trie = SecureTrie(Trie(RefcountDB(self.db), root))
134134
for k, v in STATE_DEFAULTS.items():
@@ -137,6 +137,8 @@ def __init__(self, root=b'', env=Env(), **kwargs):
137137
self.cache = {}
138138
self.log_listeners = []
139139
self.deletes = []
140+
self.changed = {}
141+
self.executing_on_head = executing_on_head
140142

141143
@property
142144
def db(self):
@@ -159,7 +161,13 @@ def add_block_header(self, block_header):
159161
def get_and_cache_account(self, address):
160162
if address in self.cache:
161163
return self.cache[address]
162-
rlpdata = self.trie.get(address)
164+
if self.executing_on_head:
165+
try:
166+
rlpdata = self.db.get(b'address:'+address)
167+
except KeyError:
168+
rlpdata = b''
169+
else:
170+
rlpdata = self.trie.get(address)
163171
if rlpdata != trie.BLANK_NODE:
164172
o = rlp.decode(rlpdata, Account, env=self.env, address=address)
165173
else:
@@ -319,13 +327,15 @@ def commit(self, allow_empties=False):
319327
if acct.touched or acct.deleted:
320328
acct.commit()
321329
self.deletes.extend(acct.storage_trie.deletes)
330+
self.changed[addr] = True
322331
if self.account_exists(addr) or allow_empties:
323332
self.trie.update(addr, rlp.encode(acct))
324-
#self.db.update(b'acct:'+addr, rlp.encode(acct))
333+
if self.executing_on_head:
334+
self.db.put(b'address:'+addr, rlp.encode(acct))
325335
else:
326336
self.trie.delete(addr)
327-
# self.db.update(b'acct:'+addr, rlp.encode(acct))
328-
# self.trie.db.
337+
if self.executing_on_head:
338+
self.db.delete(b'address:'+addr)
329339
self.deletes.extend(self.trie.deletes)
330340
self.trie.deletes = []
331341
self.cache = {}
@@ -380,7 +390,7 @@ def to_snapshot(self, root_only=False, no_prevblocks=False):
380390

381391
# Creates a state from a snapshot
382392
@classmethod
383-
def from_snapshot(cls, snapshot_data, env):
393+
def from_snapshot(cls, snapshot_data, env, executing_on_head=False):
384394
state = State(env = env)
385395
if "alloc" in snapshot_data:
386396
for addr, data in snapshot_data["alloc"].items():
@@ -425,7 +435,10 @@ def from_snapshot(cls, snapshot_data, env):
425435
else:
426436
uncles = default
427437
setattr(state, k, uncles)
438+
if executing_on_head:
439+
state.executing_on_head = True
428440
state.commit()
441+
state.changed = {}
429442
return state
430443

431444

0 commit comments

Comments
 (0)