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

Commit 8931e49

Browse files
committed
Switched state tree pruning to be based on reference counting
1 parent 16e2a51 commit 8931e49

File tree

6 files changed

+80
-45
lines changed

6 files changed

+80
-45
lines changed

ethereum/db.py

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
from ethereum import utils
22
from ethereum.slogging import get_logger
33
from rlp.utils import str_to_bytes
4+
import sys
5+
if sys.version_info.major == 2:
6+
from repoze.lru import lru_cache
7+
else:
8+
from functools import lru_cache
9+
410
log = get_logger('db')
511

612

@@ -41,25 +47,6 @@ def __eq__(self, other):
4147
def __hash__(self):
4248
return utils.big_endian_to_int(str_to_bytes(self.__repr__()))
4349

44-
def inc_refcount(self, key, value):
45-
self.put(key, value)
46-
47-
def dec_refcount(self, key):
48-
pass
49-
50-
def revert_refcount_changes(self, epoch):
51-
pass
52-
53-
def commit_refcount_changes(self, epoch):
54-
pass
55-
56-
def cleanup(self, epoch):
57-
pass
58-
59-
def put_temporarily(self, key, value):
60-
self.inc_refcount(key, value)
61-
self.dec_refcount(key)
62-
6350

6451
DB = EphemDB = _EphemDB
6552

@@ -136,21 +123,62 @@ def __eq__(self, other):
136123
def __hash__(self):
137124
return utils.big_endian_to_int(str_to_bytes(self.__repr__()))
138125

139-
def inc_refcount(self, key, value):
140-
self.put(key, value)
126+
@lru_cache(128)
127+
def add1(b):
128+
v = utils.big_endian_to_int(b)
129+
return utils.zpad(utils.encode_int(v + 1), 4)
141130

142-
def dec_refcount(self, key):
143-
pass
131+
@lru_cache(128)
132+
def sub1(b):
133+
v = utils.big_endian_to_int(b)
134+
return utils.zpad(utils.encode_int(v - 1), 4)
144135

145-
def revert_refcount_changes(self, epoch):
146-
pass
136+
class RefcountDB(BaseDB):
147137

148-
def commit_refcount_changes(self, epoch):
149-
pass
138+
def __init__(self, db):
139+
self.db = db
140+
self.kv = None
141+
142+
def get(self, key):
143+
return self.db.get(key)[4:]
150144

151-
def cleanup(self, epoch):
145+
def get_refcount(self, key):
146+
try:
147+
return utils.big_endian_to_int(self.db.get(key)[:4])
148+
except KeyError:
149+
return 0
150+
151+
def put(self, key, value):
152+
try:
153+
existing = self.db.get(key)
154+
assert existing[4:] == value
155+
self.db.put(key, add1(existing[:4]) + value)
156+
# print('putin', key, utils.big_endian_to_int(existing[:4]) + 1)
157+
except KeyError:
158+
self.db.put(key, b'\x00\x00\x00\x01' + value)
159+
# print('putin', key, 1)
160+
161+
def delete(self, key):
162+
existing = self.db.get(key)
163+
if existing[:4] == b'\x00\x00\x00\x01':
164+
# print('deletung')
165+
self.db.delete(key)
166+
else:
167+
# print(repr(existing[:4]))
168+
self.db.put(key, sub1(existing[:4]) + existing[4:])
169+
170+
def commit(self):
152171
pass
153172

154-
def put_temporarily(self, key, value):
155-
self.inc_refcount(key, value)
156-
self.dec_refcount(key)
173+
def _has_key(self, key):
174+
return key in self.db
175+
176+
def __contains__(self, key):
177+
return self._has_key(key)
178+
179+
def __eq__(self, other):
180+
return isinstance(other, self.__class__) and self.db == other.db
181+
182+
def __hash__(self):
183+
return utils.big_endian_to_int(str_to_bytes(self.__repr__()))
184+

ethereum/genesis_helpers.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
parse_as_bin, parse_as_int, normalize_address
55
from ethereum.config import Env
66
from ethereum.consensus_strategy import get_consensus_strategy
7-
from ethereum.db import OverlayDB
7+
from ethereum.db import OverlayDB, RefcountDB
88
import rlp
99
import json
1010

@@ -44,6 +44,10 @@ def state_from_genesis_declaration(genesis_data, env, block=None, allow_empties=
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)
4646
state.commit(allow_empties=allow_empties)
47+
print('deleting %d' % len(state.deletes))
48+
rdb = RefcountDB(state.db)
49+
for delete in state.deletes:
50+
rdb.delete(delete)
4751
block.header.state_root = state.trie.root_hash
4852
state.prev_headers=[block.header]
4953
return state

ethereum/pow/chain.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ethereum.pow.consensus import initialize
1919
from ethereum.genesis_helpers import mk_basic_state, state_from_genesis_declaration, \
2020
initialize_genesis_keys
21+
from ethereum.db import RefcountDB
2122

2223

2324
log = get_logger('eth.chain')
@@ -330,9 +331,10 @@ def add_block(self, block):
330331
if old_block_hash:
331332
try:
332333
deletes = self.db.get(b'deletes:'+old_block_hash)
333-
print('Deleting %d trie nodes' % (len(deletes) // 32))
334+
print('Deleting up to %d trie nodes' % (len(deletes) // 32))
335+
rdb = RefcountDB(self.db)
334336
for i in range(0, len(deletes), 32):
335-
self.db.delete(deletes[i: i+32])
337+
rdb.delete(deletes[i: i+32])
336338
self.db.delete(b'deletes:'+old_block_hash)
337339
except KeyError as e:
338340
print(e)

ethereum/securetrie.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self, t):
99

1010
def update(self, k, v):
1111
h = utils.sha3(k)
12-
self.db.put(h, k)
12+
self.db.put(h, utils.str_to_bytes(k))
1313
self.trie.update(h, v)
1414

1515
def get(self, k):

ethereum/state.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from ethereum.securetrie import SecureTrie
1111
from ethereum.config import default_config, Env
1212
from ethereum.block import FakeHeader
13-
from ethereum.db import BaseDB, EphemDB, OverlayDB
13+
from ethereum.db import BaseDB, EphemDB, OverlayDB, RefcountDB
1414
from ethereum.specials import specials as default_specials
1515
import copy
1616
import sys
@@ -64,7 +64,7 @@ def __init__(self, nonce, balance, storage, code_hash, env, address):
6464
self.address = address
6565
super(Account, self).__init__(nonce, balance, storage, code_hash)
6666
self.storage_cache = {}
67-
self.storage_trie = SecureTrie(Trie(self.env.db, prefix=address))
67+
self.storage_trie = SecureTrie(Trie(RefcountDB(self.env.db)))
6868
self.storage_trie.root_hash = self.storage
6969
self.touched = False
7070
self.existent_at_start = True
@@ -131,7 +131,7 @@ class State():
131131

132132
def __init__(self, root=b'', env=Env(), **kwargs):
133133
self.env = env
134-
self.trie = SecureTrie(Trie(self.db, root))
134+
self.trie = SecureTrie(Trie(RefcountDB(self.db), root))
135135
for k, v in STATE_DEFAULTS.items():
136136
setattr(self, k, kwargs.get(k, copy.copy(v)))
137137
self.journal = []

ethereum/trie.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def is_key_value_type(node_type):
139139

140140
class Trie(object):
141141

142-
def __init__(self, db, root_hash=BLANK_ROOT, prefix=b''):
142+
def __init__(self, db, root_hash=BLANK_ROOT):
143143
"""it also present a dictionary like interface
144144
145145
:param db key value database
@@ -148,7 +148,6 @@ def __init__(self, db, root_hash=BLANK_ROOT, prefix=b''):
148148
self.db = db # Pass in a database object directly
149149
self.set_root_hash(root_hash)
150150
self.deletes = []
151-
self.prefix = prefix
152151

153152
# def __init__(self, dbfile, root_hash=BLANK_ROOT):
154153
# """it also present a dictionary like interface
@@ -175,7 +174,7 @@ def get_root_hash(self):
175174
def _update_root_hash(self):
176175
val = rlp_encode(self.root_node)
177176
key = utils.sha3(val)
178-
self.db.put(self.prefix+key, val)
177+
self.db.put(key, str_to_bytes(val))
179178
self._root_hash = key
180179

181180
@root_hash.setter
@@ -208,7 +207,7 @@ def _delete_child_storage(self, node):
208207
elif node_type == NODE_TYPE_EXTENSION:
209208
self._delete_child_storage(self._decode_to_node(node[1]))
210209

211-
def _encode_node(self, node):
210+
def _encode_node(self, node, put_in_db=True):
212211
if node == BLANK_NODE:
213212
return BLANK_NODE
214213
# assert isinstance(node, list)
@@ -217,15 +216,16 @@ def _encode_node(self, node):
217216
return node
218217

219218
hashkey = utils.sha3(rlpnode)
220-
self.db.put(self.prefix+hashkey, rlpnode)
219+
if put_in_db:
220+
self.db.put(hashkey, str_to_bytes(rlpnode))
221221
return hashkey
222222

223223
def _decode_to_node(self, encoded):
224224
if encoded == BLANK_NODE:
225225
return BLANK_NODE
226226
if isinstance(encoded, list):
227227
return encoded
228-
o = rlp.decode(self.db.get(self.prefix+encoded))
228+
o = rlp.decode(self.db.get(encoded))
229229
return o
230230

231231
def _get_node_type(self, node):
@@ -600,7 +600,7 @@ def _delete_node_storage(self, node):
600600
if node == BLANK_NODE:
601601
return
602602
# assert isinstance(node, list)
603-
encoded = self._encode_node(node)
603+
encoded = self._encode_node(node, put_in_db=False)
604604
if len(encoded) < 32:
605605
return
606606
"""
@@ -609,6 +609,7 @@ def _delete_node_storage(self, node):
609609
thus we can not safely delete nodes for now
610610
"""
611611
self.deletes.append(encoded)
612+
# print('del', encoded, self.db.get_refcount(encoded))
612613

613614
def _delete(self, node, key):
614615
""" update item inside a node

0 commit comments

Comments
 (0)