diff --git a/apps/cic-cache/cic_cache/runnable/daemons/filters/base.py b/apps/cic-cache/cic_cache/runnable/daemons/filters/base.py index 860296de..26e5b31d 100644 --- a/apps/cic-cache/cic_cache/runnable/daemons/filters/base.py +++ b/apps/cic-cache/cic_cache/runnable/daemons/filters/base.py @@ -10,6 +10,6 @@ class TagSyncFilter: def __str__(self): - if tag_domain == None: + if self.tag_domain == None: return self.tag_name return '{}.{}'.format(self.tag_domain, self.tag_name) diff --git a/apps/cic-cache/cic_cache/runnable/daemons/filters/erc20.py b/apps/cic-cache/cic_cache/runnable/daemons/filters/erc20.py index 3a5f4900..0dc618e0 100644 --- a/apps/cic-cache/cic_cache/runnable/daemons/filters/erc20.py +++ b/apps/cic-cache/cic_cache/runnable/daemons/filters/erc20.py @@ -15,7 +15,7 @@ from cic_eth_registry.error import ( ) # local imports -from .base import SyncFilter +from .base import TagSyncFilter from cic_cache import db as cic_cache_db logg = logging.getLogger().getChild(__name__) @@ -24,7 +24,7 @@ logg = logging.getLogger().getChild(__name__) class ERC20TransferFilter(TagSyncFilter): def __init__(self, chain_spec): - super(TagSyncFilter, self).__init__('transfer', 'erc20') + super(ERC20TransferFilter, self).__init__('transfer', domain='erc20') self.chain_spec = chain_spec @@ -47,6 +47,9 @@ class ERC20TransferFilter(TagSyncFilter): except RequestMismatchException: logg.debug('erc20 match but not a transfer, skipping') return False + except ValueError: + logg.debug('erc20 match but bogus data, skipping') + return False token_sender = tx.outputs[0] token_recipient = transfer_data[0] @@ -68,7 +71,13 @@ class ERC20TransferFilter(TagSyncFilter): tx.status == Status.SUCCESS, block.timestamp, ) - #db_session.flush() + db_session.flush() + cic_cache_db.tag_transaction( + db_session, + tx.hash, + self.tag_name, + domain=self.tag_domain, + ) db_session.commit() return True diff --git a/apps/cic-cache/cic_cache/runnable/daemons/tracker.py b/apps/cic-cache/cic_cache/runnable/daemons/tracker.py index 270e1f6a..ceac150b 100644 --- a/apps/cic-cache/cic_cache/runnable/daemons/tracker.py +++ b/apps/cic-cache/cic_cache/runnable/daemons/tracker.py @@ -7,9 +7,10 @@ import argparse import sys import re -# third-party imports +# external imports import confini import celery +import sqlalchemy import rlp import cic_base.config import cic_base.log @@ -34,7 +35,10 @@ from chainsyncer.driver import ( from chainsyncer.db.models.base import SessionBase # local imports -from cic_cache.db import dsn_from_config +from cic_cache.db import ( + dsn_from_config, + add_tag, + ) from cic_cache.runnable.daemons.filters import ( ERC20TransferFilter, ) @@ -59,6 +63,17 @@ chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC')) cic_base.rpc.setup(chain_spec, config.get('ETH_PROVIDER')) +def register_filter_tags(filters, session): + for f in filters: + tag = f.tag() + try: + add_tag(session, tag[0], domain=tag[1]) + session.commit() + logg.info('added tag name "{}" domain "{}"'.format(tag[0], tag[1])) + except sqlalchemy.exc.IntegrityError: + logg.debug('already have tag name "{}" domain "{}"'.format(tag[0], tag[1])) + + def main(): # Connect to blockchain with chainlib rpc = RPCConnection.connect(chain_spec, 'default') @@ -98,10 +113,19 @@ def main(): erc20_transfer_filter = ERC20TransferFilter(chain_spec) + filters = [ + erc20_transfer_filter, + ] + + session = SessionBase.create_session() + register_filter_tags(filters, session) + session.close() + i = 0 for syncer in syncers: logg.debug('running syncer index {}'.format(i)) - syncer.add_filter(erc20_transfer_filter) + for f in filters: + syncer.add_filter(f) r = syncer.loop(int(config.get('SYNCER_LOOP_INTERVAL')), rpc) sys.stderr.write("sync {} done at block {}\n".format(syncer, r)) diff --git a/apps/cic-cache/config/database.ini b/apps/cic-cache/config/database.ini index e34a1ff5..7c5836a2 100644 --- a/apps/cic-cache/config/database.ini +++ b/apps/cic-cache/config/database.ini @@ -6,4 +6,4 @@ HOST=localhost PORT=5432 ENGINE=postgresql DRIVER=psycopg2 -DEBUG= +DEBUG=0 diff --git a/apps/cic-cache/config/test/cic.ini b/apps/cic-cache/config/test/cic.ini index f52b27bd..d985ae3e 100644 --- a/apps/cic-cache/config/test/cic.ini +++ b/apps/cic-cache/config/test/cic.ini @@ -1,2 +1,4 @@ [cic] registry_address = +chain_spec = +trust_address = diff --git a/apps/cic-cache/config/test/database.ini b/apps/cic-cache/config/test/database.ini index 960863a6..113c1fa4 100644 --- a/apps/cic-cache/config/test/database.ini +++ b/apps/cic-cache/config/test/database.ini @@ -6,4 +6,4 @@ HOST=localhost PORT=5432 ENGINE=sqlite DRIVER=pysqlite -DEBUG= +DEBUG=1 diff --git a/apps/cic-cache/test_requirements.txt b/apps/cic-cache/test_requirements.txt index b6ca05ac..dce79ea1 100644 --- a/apps/cic-cache/test_requirements.txt +++ b/apps/cic-cache/test_requirements.txt @@ -4,3 +4,10 @@ pytest-mock==3.3.1 pysqlite3==0.4.3 sqlparse==0.4.1 pytest-celery==0.0.0a1 +eth_tester==0.5.0b3 +py-evm==0.3.0a20 +web3==5.12.2 +cic-eth-registry~=0.5.5a3 +giftable-erc20-token~=0.0.8a10 +eth-address-index~=0.1.1a10 +sarafu-faucet~=0.0.3a1 diff --git a/apps/cic-cache/tests/conftest.py b/apps/cic-cache/tests/conftest.py index d45cf8ff..00db9ae0 100644 --- a/apps/cic-cache/tests/conftest.py +++ b/apps/cic-cache/tests/conftest.py @@ -3,7 +3,7 @@ import os import sys import datetime -# third-party imports +# external imports import pytest # local imports diff --git a/apps/cic-cache/tests/filters/conftest.py b/apps/cic-cache/tests/filters/conftest.py new file mode 100644 index 00000000..50fa9b64 --- /dev/null +++ b/apps/cic-cache/tests/filters/conftest.py @@ -0,0 +1,3 @@ +from chainlib.eth.pytest import * +from cic_eth_registry.pytest.fixtures_tokens import * + diff --git a/apps/cic-cache/tests/filters/test_erc20.py b/apps/cic-cache/tests/filters/test_erc20.py new file mode 100644 index 00000000..d7582ed1 --- /dev/null +++ b/apps/cic-cache/tests/filters/test_erc20.py @@ -0,0 +1,69 @@ +# standard imports +import os +import datetime +import logging +import json + +# external imports +import pytest +from sqlalchemy import text +from chainlib.eth.tx import Tx +from chainlib.eth.block import Block +from chainlib.chain import ChainSpec +from hexathon import ( + strip_0x, + add_0x, + ) + +# local imports +from cic_cache.db import add_tag +from cic_cache.runnable.daemons.filters.erc20 import ERC20TransferFilter + +logg = logging.getLogger() + + +def test_cache( + eth_rpc, + foo_token, + init_database, + list_defaults, + list_actors, + tags, + ): + + chain_spec = ChainSpec('foo', 'bar', 42, 'baz') + + fltr = ERC20TransferFilter(chain_spec) + + add_tag(init_database, fltr.tag_name, domain=fltr.tag_domain) + + data = 'a9059cbb' + data += strip_0x(list_actors['alice']) + data += '1000'.ljust(64, '0') + + block = Block({ + 'hash': os.urandom(32).hex(), + 'number': 42, + 'timestamp': datetime.datetime.utcnow().timestamp(), + 'transactions': [], + }) + + tx = Tx({ + 'to': foo_token, + 'from': list_actors['bob'], + 'data': data, + 'value': 0, + 'hash': os.urandom(32).hex(), + 'nonce': 13, + 'gasPrice': 10000000, + 'gas': 123456, + }) + block.txs.append(tx) + tx.block = block + + r = fltr.filter(eth_rpc, block, tx, db_session=init_database) + assert r + + s = text("SELECT x.tx_hash FROM tag a INNER JOIN tag_tx_link l ON l.tag_id = a.id INNER JOIN tx x ON x.id = l.tx_id WHERE a.domain = :a AND a.value = :b") + r = init_database.execute(s, {'a': fltr.tag_domain, 'b': fltr.tag_name}).fetchone() + assert r[0] == tx.hash