eth-cache

Ethereum chain data caching tools
Log | Files | Refs | LICENSE

commit 18384d95b35b66f064306f8075048b7acfb79bd8
parent 8e3490c4d35014cfbfa5dd153bd60c20a68bb744
Author: lash <dev@holbrook.no>
Date:   Sun, 16 Jun 2024 23:04:52 +0100

Factor out test setup, implement lmdb class

Diffstat:
Aeth_cache/store/lmdb.py | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mrequirements.txt | 2+-
Msetup.py | 2+-
Mtest_requirements.txt | 9+++++----
Mtests/test_basic.py | 52+++++++++++++---------------------------------------
Atests/test_lmdb.py | 24++++++++++++++++++++++++
Atests/util.py | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 178 insertions(+), 45 deletions(-)

diff --git a/eth_cache/store/lmdb.py b/eth_cache/store/lmdb.py @@ -0,0 +1,66 @@ +# standard imports +import os +import json +import logging + +# external imports +import lmdb +from hexathon import strip_0x +from chainlib.eth.tx import ( + Tx, + pack, + ) + + +logg = logging.getLogger(__name__) + +default_base_dir = '/var/lib' + + +def chain_dir_for(chain_spec, base_dir=default_base_dir): + chain_dir = os.path.join(base_dir, str(chain_spec).replace(':', '/')) + return os.path.join(chain_dir, 'eth_cache') + + +class LmdbStore: + + def __init__(self, chain_spec, cache_root=default_base_dir, address_rules=None): + # TODO: perhaps common for all + self.chain_spec = chain_spec + self.address_rules = address_rules + # TODO: perhaps common for all fs + self.chain_dir = chain_dir_for(chain_spec, cache_root) + self.cache_dir = self.chain_dir + os.makedirs(self.cache_dir, exist_ok=True) + self.block_src_path = os.path.join(self.cache_dir, 'block', 'src') + self.block_num_path = os.path.join(self.cache_dir, 'block', 'num') + self.block_hash_path = os.path.join(self.cache_dir, 'block', 'hash') + self.tx_path = os.path.join(self.cache_dir, 'tx', 'src') + self.tx_raw_path = os.path.join(self.cache_dir, 'tx', 'raw') + self.rcpt_path = os.path.join(self.cache_dir, 'rcpt', 'src') + self.rcpt_raw_path = os.path.join(self.cache_dir, 'rcpt', 'raw') + self.address_path = os.path.join(self.cache_dir, 'address') + self.db = lmdb.open(self.cache_dir, create=True) + + + def __to_path_key(self, path, k): + if path[len(path)-1] != '/': + path += '/' + return path.encode('utf-8') + k + + + def put_tx(self, tx, include_data=False): + raw = pack(tx.src, self.chain_spec) + tx_hash_dirnormal = strip_0x(tx.hash).upper() + tx_hash_bytes = bytes.fromhex(tx_hash_dirnormal) + k = self.__to_path_key('tx_raw', tx_hash_bytes) + with self.db.begin(write=True) as tx: + tx.put(k, raw) + + + def get_tx(self, tx_hash): + self.__to_path_key(self, path, key) + + + def __str__(self): + return 'RockDbStore: root {}'.format(self.cache_dir) diff --git a/requirements.txt b/requirements.txt @@ -1,4 +1,4 @@ hexathon~=0.1.7 jsonrpc-std~=0.1.0 leveldir~=0.3.0 -chainlib-eth~=0.6.0 +chainlib-eth~=0.6.3 diff --git a/setup.py b/setup.py @@ -1,5 +1,5 @@ from setuptools import setup -import configparser +#import configparser import os diff --git a/test_requirements.txt b/test_requirements.txt @@ -1,5 +1,6 @@ -eth_tester==0.5.0b3 -py-evm==0.3.0a20 -rlp==2.0.1 -pytest==6.0.1 +eth_tester==0.10.0b4 +py-evm==0.10.0b4 +rlp==3.0.0 +#pytest==6.0.1 coverage==5.5 +#setuptools==70.0.0 diff --git a/tests/test_basic.py b/tests/test_basic.py @@ -6,64 +6,38 @@ import logging import json # external imports -from chainlib.chain import ChainSpec +from chainlib.eth.address import is_same_address +from hexathon import strip_0x +from chainlib.eth.nonce import RPCNonceOracle from chainlib.eth.gas import ( Gas, OverrideGasOracle, ) -from chainlib.eth.nonce import RPCNonceOracle -from chainlib.eth.unittest.ethtester import EthTesterCase -from chainlib.eth.tx import ( - transaction, - receipt, - TxFormat, - Tx, - ) from chainlib.eth.block import ( block_by_hash, block_by_number, Block, ) -from chainlib.eth.address import is_same_address -from hexathon import strip_0x - +from chainlib.eth.tx import ( + transaction, + Tx, + ) + # local imports from eth_cache.store.file import FileStore from eth_cache.rpc import CacheRPC +from tests.util import TestCache + logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() -class TestCache(EthTesterCase): +class TestCacheBasic(TestCache): def setUp(self): - super(TestCache, self).setUp() - fp = tempfile.mkdtemp() - self.cache_dir = fp - - class Applier: - def apply_rules_addresses(self, sender, recipient, address): - return True - - self.store = FileStore(self.chain_spec, cache_root=self.cache_dir, address_rules=Applier()) - nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) - gas_oracle = OverrideGasOracle(price=100000000000, limit=30000) - c = Gas(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle) - (tx_hash, o) = c.create(self.accounts[0], self.accounts[1], 1024) - r = self.rpc.do(o) - - o = transaction(tx_hash) - tx_src = self.rpc.do(o) - - o = receipt(tx_hash) - rcpt_src = self.rpc.do(o) - - o = block_by_hash(tx_src['block_hash']) - block_src = self.rpc.do(o) - - self.block = Block(block_src) - self.tx = Tx(tx_src, block=self.block, rcpt=rcpt_src) + super(TestCacheBasic, self).setUp() + self.store = FileStore(self.chain_spec, cache_root=self.cache_dir, address_rules=self.address_rules) def tearDown(self): diff --git a/tests/test_lmdb.py b/tests/test_lmdb.py @@ -0,0 +1,24 @@ +# standard imports +import unittest + +# local imports +from eth_cache.store.lmdb import LmdbStore +from tests.util import TestCache + + +class TestCacheBasic(TestCache): + + def setUp(self): + super(TestCacheBasic, self).setUp() + self.store = LmdbStore(self.chain_spec, cache_root=self.cache_dir, address_rules=self.address_rules) + + + def test_tx(self): + self.store.put_tx(self.tx, include_data=True) + #j = self.store.get_tx(self.tx.hash) + #tx = json.loads(j) + #self.assertTrue(is_same_address(tx['hash'], self.tx.hash)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/util.py b/tests/util.py @@ -0,0 +1,68 @@ +# standard imports +import tempfile +import shutil +import unittest + +# external imports +from chainlib.chain import ChainSpec +from chainlib.eth.gas import ( + Gas, + OverrideGasOracle, + ) +from chainlib.eth.nonce import RPCNonceOracle +from chainlib.eth.tx import ( + transaction, + receipt, + TxFormat, + Tx, + ) +from chainlib.eth.block import ( + block_by_hash, + Block, + ) +from chainlib.eth.dialect import DialectFilter as EthDialectFilter + +from chainlib.eth.unittest.ethtester import EthTesterCase + +# TODO: 2024-06-16 suddenly state_root value is binary, which breaks the json serializer. this seems to be a bug in the eth-tester, and this code should probably be moved to chainlib-eth unittest and implemented by default in test cases +class DialectFilter(EthDialectFilter): + + def apply_src(self, src, dialect_filter=None): + + for k in src.keys(): + if type(src[k]) == bytes: + src[k] = '0x' + src[k].hex() + + return src + + +class TestCache(EthTesterCase): + + def setUp(self): + super(TestCache, self).setUp() + fp = tempfile.mkdtemp() + self.cache_dir = fp + + class Applier: + def apply_rules_addresses(self, sender, recipient, address): + return True + + self.address_rules = Applier() + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + gas_oracle = OverrideGasOracle(price=100000000000, limit=30000) + c = Gas(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle) + (tx_hash, o) = c.create(self.accounts[0], self.accounts[1], 1024) + r = self.rpc.do(o) + + o = transaction(tx_hash) + tx_src = self.rpc.do(o) + + o = receipt(tx_hash) + rcpt_src = self.rpc.do(o) + + o = block_by_hash(tx_src['block_hash']) + block_src = self.rpc.do(o) + + fltr = DialectFilter() + self.block = Block(block_src, dialect_filter=fltr) + self.tx = Tx(tx_src, block=self.block, rcpt=rcpt_src, dialect_filter=fltr)