Compare commits
33 Commits
master
...
lash/demur
Author | SHA1 | Date | |
---|---|---|---|
|
efa23797f3 | ||
|
2c0f65ed73 | ||
|
3f6fbc1c61 | ||
|
d26af206cb | ||
|
36085f218f | ||
|
d1d680c9aa | ||
|
abd4024322 | ||
|
644b9ec4ec | ||
|
bc52e46620 | ||
|
ba729a0a54 | ||
|
cb4927edc9 | ||
|
185458e320 | ||
|
d9214ddd62 | ||
|
b5d6d80d8e | ||
|
2f09ac9110 | ||
|
bc8851ad06 | ||
|
46840040a0 | ||
|
4e18060589 | ||
|
08a77f9818 | ||
|
5fab939270 | ||
|
51109db487 | ||
|
f44e2c8b45 | ||
|
40a7eec6ad | ||
|
6a75ce37c4 | ||
|
9b2f2ab0b1 | ||
|
98a38b7117 | ||
|
a2ca61355d | ||
|
b0638f2262 | ||
|
ea4c68f311 | ||
|
ba0dc9371e | ||
|
03ac6633a2 | ||
|
4dc8dff369 | ||
|
a74e69aeb3 |
1
apps/cic-eth-aux/erc20-demurrage-token/MANIFEST.in
Normal file
1
apps/cic-eth-aux/erc20-demurrage-token/MANIFEST.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
include *requirements.txt
|
@ -0,0 +1,53 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
import celery
|
||||||
|
from erc20_demurrage_token.demurrage import DemurrageCalculator
|
||||||
|
from chainlib.connection import RPCConnection
|
||||||
|
from chainlib.chain import ChainSpec
|
||||||
|
from chainlib.eth.constant import ZERO_ADDRESS
|
||||||
|
from cic_eth_registry import CICRegistry
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
celery_app = celery.current_app
|
||||||
|
|
||||||
|
|
||||||
|
class NoopCalculator:
|
||||||
|
|
||||||
|
def amount_since(self, amount, timestamp):
|
||||||
|
logg.debug('noopcalculator amount {} timestamp {}'.format(amount, timestamp))
|
||||||
|
return amount
|
||||||
|
|
||||||
|
|
||||||
|
class DemurrageCalculationTask(celery.Task):
|
||||||
|
|
||||||
|
demurrage_token_calcs = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_token(cls, rpc, chain_spec, token_symbol, sender_address=ZERO_ADDRESS):
|
||||||
|
registry = CICRegistry(chain_spec, rpc)
|
||||||
|
token_address = registry.by_name(token_symbol, sender_address=sender_address)
|
||||||
|
try:
|
||||||
|
c = DemurrageCalculator.from_contract(rpc, chain_spec, token_address, sender_address=sender_address)
|
||||||
|
logg.info('found demurrage calculator for ERC20 {} @ {}'.format(token_symbol, token_address))
|
||||||
|
except:
|
||||||
|
logg.warning('Token {} at address {} does not appear to be a demurrage contract. Calls to balance adjust for this token will always return the same amount'.format(token_symbol, token_address))
|
||||||
|
c = NoopCalculator()
|
||||||
|
|
||||||
|
cls.demurrage_token_calcs[token_symbol] = c
|
||||||
|
|
||||||
|
|
||||||
|
@celery_app.task(bind=True, base=DemurrageCalculationTask)
|
||||||
|
def get_adjusted_balance(self, token_symbol, amount, timestamp):
|
||||||
|
c = self.demurrage_token_calcs[token_symbol]
|
||||||
|
return c.amount_since(amount, timestamp)
|
||||||
|
|
||||||
|
|
||||||
|
def aux_setup(rpc, config, sender_address=ZERO_ADDRESS):
|
||||||
|
chain_spec_str = config.get('CIC_CHAIN_SPEC')
|
||||||
|
chain_spec = ChainSpec.from_chain_str(chain_spec_str)
|
||||||
|
token_symbol = config.get('CIC_DEFAULT_TOKEN_SYMBOL')
|
||||||
|
|
||||||
|
DemurrageCalculationTask.register_token(rpc, chain_spec, token_symbol, sender_address=sender_address)
|
@ -0,0 +1,30 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
import celery
|
||||||
|
from cic_eth.api.base import ApiBase
|
||||||
|
|
||||||
|
app = celery.current_app
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Api(ApiBase):
|
||||||
|
|
||||||
|
def get_adjusted_balance(self, token_symbol, balance, timestamp):
|
||||||
|
s = celery.signature(
|
||||||
|
'cic_eth_aux.erc20_demurrage_token.get_adjusted_balance',
|
||||||
|
[
|
||||||
|
token_symbol,
|
||||||
|
balance,
|
||||||
|
timestamp,
|
||||||
|
],
|
||||||
|
queue=None,
|
||||||
|
)
|
||||||
|
if self.callback_param != None:
|
||||||
|
s.link(self.callback_success)
|
||||||
|
s.link.on_error(self.callback_error)
|
||||||
|
|
||||||
|
t = s.apply_async(queue=self.queue)
|
||||||
|
return t
|
5
apps/cic-eth-aux/erc20-demurrage-token/requirements.txt
Normal file
5
apps/cic-eth-aux/erc20-demurrage-token/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
celery==4.4.7
|
||||||
|
erc20-demurrage-token~=0.0.2a3
|
||||||
|
cic-eth-registry~=0.5.6a1
|
||||||
|
chainlib~=0.0.5a1
|
||||||
|
cic_eth~=0.12.0a2
|
30
apps/cic-eth-aux/erc20-demurrage-token/setup.cfg
Normal file
30
apps/cic-eth-aux/erc20-demurrage-token/setup.cfg
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[metadata]
|
||||||
|
name = cic-eth-aux-erc20-demurrage-token
|
||||||
|
version = 0.0.2a4
|
||||||
|
description = cic-eth tasks supporting erc20 demurrage token
|
||||||
|
author = Louis Holbrook
|
||||||
|
author_email = dev@holbrook.no
|
||||||
|
url = https://gitlab.com/ccicnet/erc20-demurrage-token
|
||||||
|
keywords =
|
||||||
|
ethereum
|
||||||
|
blockchain
|
||||||
|
cryptocurrency
|
||||||
|
erc20
|
||||||
|
classifiers =
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Operating System :: OS Independent
|
||||||
|
Development Status :: 3 - Alpha
|
||||||
|
Environment :: No Input/Output (Daemon)
|
||||||
|
Intended Audience :: Developers
|
||||||
|
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||||
|
Topic :: Internet
|
||||||
|
#Topic :: Blockchain :: EVM
|
||||||
|
license = GPL3
|
||||||
|
licence_files =
|
||||||
|
LICENSE
|
||||||
|
|
||||||
|
[options]
|
||||||
|
include_package_data = True
|
||||||
|
python_requires = >= 3.6
|
||||||
|
packages =
|
||||||
|
cic_eth_aux.erc20_demurrage_token
|
25
apps/cic-eth-aux/erc20-demurrage-token/setup.py
Normal file
25
apps/cic-eth-aux/erc20-demurrage-token/setup.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
requirements = []
|
||||||
|
f = open('requirements.txt', 'r')
|
||||||
|
while True:
|
||||||
|
l = f.readline()
|
||||||
|
if l == '':
|
||||||
|
break
|
||||||
|
requirements.append(l.rstrip())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
test_requirements = []
|
||||||
|
f = open('test_requirements.txt', 'r')
|
||||||
|
while True:
|
||||||
|
l = f.readline()
|
||||||
|
if l == '':
|
||||||
|
break
|
||||||
|
test_requirements.append(l.rstrip())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
install_requires=requirements,
|
||||||
|
tests_require=test_requirements,
|
||||||
|
)
|
12
apps/cic-eth-aux/erc20-demurrage-token/test_requirements.txt
Normal file
12
apps/cic-eth-aux/erc20-demurrage-token/test_requirements.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
pytest==6.0.1
|
||||||
|
pytest-celery==0.0.0a1
|
||||||
|
pytest-mock==3.3.1
|
||||||
|
pytest-cov==2.10.1
|
||||||
|
eth-tester==0.5.0b3
|
||||||
|
py-evm==0.3.0a20
|
||||||
|
SQLAlchemy==1.3.20
|
||||||
|
cic-eth~=0.12.0a1
|
||||||
|
liveness~=0.0.1a7
|
||||||
|
eth-accounts-index==0.0.12a1
|
||||||
|
eth-contract-registry==0.5.6a1
|
||||||
|
eth-address-index==0.1.2a1
|
88
apps/cic-eth-aux/erc20-demurrage-token/tests/conftest.py
Normal file
88
apps/cic-eth-aux/erc20-demurrage-token/tests/conftest.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# external imports
|
||||||
|
import celery
|
||||||
|
from chainlib.eth.pytest.fixtures_chain import *
|
||||||
|
from chainlib.eth.pytest.fixtures_ethtester import *
|
||||||
|
from cic_eth_registry.pytest.fixtures_contracts import *
|
||||||
|
from cic_eth_registry.pytest.fixtures_tokens import *
|
||||||
|
from erc20_demurrage_token.unittest.base import TestTokenDeploy
|
||||||
|
from erc20_demurrage_token.token import DemurrageToken
|
||||||
|
from eth_token_index.index import TokenUniqueSymbolIndex
|
||||||
|
from eth_address_declarator.declarator import AddressDeclarator
|
||||||
|
|
||||||
|
# cic-eth imports
|
||||||
|
from cic_eth.pytest.fixtures_celery import *
|
||||||
|
from cic_eth.pytest.fixtures_token import *
|
||||||
|
from cic_eth.pytest.fixtures_config import *
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def demurrage_token(
|
||||||
|
default_chain_spec,
|
||||||
|
eth_rpc,
|
||||||
|
token_registry,
|
||||||
|
contract_roles,
|
||||||
|
eth_signer,
|
||||||
|
):
|
||||||
|
d = TestTokenDeploy(eth_rpc, token_symbol='BAR', token_name='Bar Token')
|
||||||
|
nonce_oracle = RPCNonceOracle(contract_roles['CONTRACT_DEPLOYER'], conn=eth_rpc)
|
||||||
|
c = DemurrageToken(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
|
||||||
|
token_address = d.deploy(eth_rpc, contract_roles['CONTRACT_DEPLOYER'], c, 'SingleNocap')
|
||||||
|
logg.debug('demurrage token contract "BAR" deployed to {}'.format(token_address))
|
||||||
|
|
||||||
|
return token_address
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def demurrage_token_symbol(
|
||||||
|
default_chain_spec,
|
||||||
|
eth_rpc,
|
||||||
|
demurrage_token,
|
||||||
|
contract_roles,
|
||||||
|
):
|
||||||
|
|
||||||
|
c = DemurrageToken(default_chain_spec)
|
||||||
|
o = c.symbol(demurrage_token, sender_address=contract_roles['CONTRACT_DEPLOYER'])
|
||||||
|
r = eth_rpc.do(o)
|
||||||
|
return c.parse_symbol(r)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def demurrage_token_declaration(
|
||||||
|
foo_token_declaration,
|
||||||
|
):
|
||||||
|
return foo_token_declaration
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def register_demurrage_token(
|
||||||
|
default_chain_spec,
|
||||||
|
token_registry,
|
||||||
|
eth_rpc,
|
||||||
|
eth_signer,
|
||||||
|
register_lookups,
|
||||||
|
contract_roles,
|
||||||
|
demurrage_token_declaration,
|
||||||
|
demurrage_token,
|
||||||
|
address_declarator,
|
||||||
|
):
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(contract_roles['CONTRACT_DEPLOYER'], eth_rpc)
|
||||||
|
|
||||||
|
c = TokenUniqueSymbolIndex(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
|
||||||
|
(tx_hash_hex, o) = c.register(token_registry, contract_roles['CONTRACT_DEPLOYER'], demurrage_token)
|
||||||
|
eth_rpc.do(o)
|
||||||
|
o = receipt(tx_hash_hex)
|
||||||
|
r = eth_rpc.do(o)
|
||||||
|
assert r['status'] == 1
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(contract_roles['TRUSTED_DECLARATOR'], eth_rpc)
|
||||||
|
c = AddressDeclarator(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
|
||||||
|
(tx_hash_hex, o) = c.add_declaration(address_declarator, contract_roles['TRUSTED_DECLARATOR'], demurrage_token, demurrage_token_declaration)
|
||||||
|
|
||||||
|
eth_rpc.do(o)
|
||||||
|
o = receipt(tx_hash_hex)
|
||||||
|
r = eth_rpc.do(o)
|
||||||
|
assert r['status'] == 1
|
||||||
|
|
||||||
|
return token_registry
|
||||||
|
|
@ -0,0 +1,69 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
import copy
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
import celery
|
||||||
|
|
||||||
|
# cic-eth imports
|
||||||
|
from cic_eth_aux.erc20_demurrage_token import (
|
||||||
|
DemurrageCalculationTask,
|
||||||
|
aux_setup,
|
||||||
|
)
|
||||||
|
from cic_eth_aux.erc20_demurrage_token.api import Api as AuxApi
|
||||||
|
|
||||||
|
logg = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def test_demurrage_calulate_task(
|
||||||
|
default_chain_spec,
|
||||||
|
eth_rpc,
|
||||||
|
cic_registry,
|
||||||
|
celery_session_worker,
|
||||||
|
register_demurrage_token,
|
||||||
|
demurrage_token_symbol,
|
||||||
|
contract_roles,
|
||||||
|
load_config,
|
||||||
|
):
|
||||||
|
|
||||||
|
config = copy.copy(load_config)
|
||||||
|
config.add(str(default_chain_spec), 'CIC_CHAIN_SPEC', exists_ok=True)
|
||||||
|
config.add(demurrage_token_symbol, 'CIC_DEFAULT_TOKEN_SYMBOL', exists_ok=True)
|
||||||
|
aux_setup(eth_rpc, load_config, sender_address=contract_roles['CONTRACT_DEPLOYER'])
|
||||||
|
|
||||||
|
since = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
|
||||||
|
s = celery.signature(
|
||||||
|
'cic_eth_aux.erc20_demurrage_token.get_adjusted_balance',
|
||||||
|
[
|
||||||
|
demurrage_token_symbol,
|
||||||
|
1000,
|
||||||
|
since.timestamp(),
|
||||||
|
],
|
||||||
|
queue=None,
|
||||||
|
)
|
||||||
|
t = s.apply_async()
|
||||||
|
r = t.get_leaf()
|
||||||
|
assert t.successful()
|
||||||
|
assert r == 980
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_demurrage_calculate_api(
|
||||||
|
default_chain_spec,
|
||||||
|
eth_rpc,
|
||||||
|
cic_registry,
|
||||||
|
celery_session_worker,
|
||||||
|
register_demurrage_token,
|
||||||
|
demurrage_token_symbol,
|
||||||
|
contract_roles,
|
||||||
|
load_config,
|
||||||
|
):
|
||||||
|
|
||||||
|
api = AuxApi(str(default_chain_spec), queue=None)
|
||||||
|
since = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
|
||||||
|
t = api.get_adjusted_balance(demurrage_token_symbol, 1000, since.timestamp())
|
||||||
|
r = t.get_leaf()
|
||||||
|
assert t.successful()
|
||||||
|
assert r == 980
|
||||||
|
|
@ -1,2 +1,2 @@
|
|||||||
include *requirements.txt
|
include *requirements.txt config/test/*
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import celery
|
|||||||
from chainlib.chain import ChainSpec
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
|
from cic_eth.api.base import ApiBase
|
||||||
from cic_eth.enum import LockEnum
|
from cic_eth.enum import LockEnum
|
||||||
|
|
||||||
app = celery.current_app
|
app = celery.current_app
|
||||||
@ -18,47 +19,7 @@ app = celery.current_app
|
|||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Api:
|
class Api(ApiBase):
|
||||||
"""Creates task chains to perform well-known CIC operations.
|
|
||||||
|
|
||||||
Each method that sends tasks returns details about the root task. The root task uuid can be provided in the callback, to enable to caller to correlate the result with individual calls. It can also be used to independently poll the completion of a task chain.
|
|
||||||
|
|
||||||
:param callback_param: Static value to pass to callback
|
|
||||||
:type callback_param: str
|
|
||||||
:param callback_task: Callback task that executes callback_param call. (Must be included by the celery worker)
|
|
||||||
:type callback_task: string
|
|
||||||
:param queue: Name of worker queue to submit tasks to
|
|
||||||
:type queue: str
|
|
||||||
"""
|
|
||||||
def __init__(self, chain_str, queue='cic-eth', callback_param=None, callback_task='cic_eth.callbacks.noop.noop', callback_queue=None):
|
|
||||||
self.chain_str = chain_str
|
|
||||||
self.chain_spec = ChainSpec.from_chain_str(chain_str)
|
|
||||||
self.callback_param = callback_param
|
|
||||||
self.callback_task = callback_task
|
|
||||||
self.queue = queue
|
|
||||||
logg.debug('api using queue {}'.format(self.queue))
|
|
||||||
self.callback_success = None
|
|
||||||
self.callback_error = None
|
|
||||||
if callback_queue == None:
|
|
||||||
callback_queue=self.queue
|
|
||||||
|
|
||||||
if callback_param != None:
|
|
||||||
self.callback_success = celery.signature(
|
|
||||||
callback_task,
|
|
||||||
[
|
|
||||||
callback_param,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
queue=callback_queue,
|
|
||||||
)
|
|
||||||
self.callback_error = celery.signature(
|
|
||||||
callback_task,
|
|
||||||
[
|
|
||||||
callback_param,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
queue=callback_queue,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def default_token(self):
|
def default_token(self):
|
||||||
|
52
apps/cic-eth/cic_eth/api/base.py
Normal file
52
apps/cic-eth/cic_eth/api/base.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# standard imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# external imports
|
||||||
|
import celery
|
||||||
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class ApiBase:
|
||||||
|
"""Creates task chains to perform well-known CIC operations.
|
||||||
|
|
||||||
|
Each method that sends tasks returns details about the root task. The root task uuid can be provided in the callback, to enable to caller to correlate the result with individual calls. It can also be used to independently poll the completion of a task chain.
|
||||||
|
|
||||||
|
:param callback_param: Static value to pass to callback
|
||||||
|
:type callback_param: str
|
||||||
|
:param callback_task: Callback task that executes callback_param call. (Must be included by the celery worker)
|
||||||
|
:type callback_task: string
|
||||||
|
:param queue: Name of worker queue to submit tasks to
|
||||||
|
:type queue: str
|
||||||
|
"""
|
||||||
|
def __init__(self, chain_str, queue='cic-eth', callback_param=None, callback_task='cic_eth.callbacks.noop.noop', callback_queue=None):
|
||||||
|
self.chain_str = chain_str
|
||||||
|
self.chain_spec = ChainSpec.from_chain_str(chain_str)
|
||||||
|
self.callback_param = callback_param
|
||||||
|
self.callback_task = callback_task
|
||||||
|
self.queue = queue
|
||||||
|
logg.debug('api using queue {}'.format(self.queue))
|
||||||
|
self.callback_success = None
|
||||||
|
self.callback_error = None
|
||||||
|
if callback_queue == None:
|
||||||
|
callback_queue=self.queue
|
||||||
|
|
||||||
|
if callback_param != None:
|
||||||
|
self.callback_success = celery.signature(
|
||||||
|
callback_task,
|
||||||
|
[
|
||||||
|
callback_param,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
queue=callback_queue,
|
||||||
|
)
|
||||||
|
self.callback_error = celery.signature(
|
||||||
|
callback_task,
|
||||||
|
[
|
||||||
|
callback_param,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
queue=callback_queue,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
0
apps/cic-eth/cic_eth/pytest/__init__.py
Normal file
0
apps/cic-eth/cic_eth/pytest/__init__.py
Normal file
@ -37,7 +37,7 @@ def celery_includes():
|
|||||||
'cic_eth.eth.account',
|
'cic_eth.eth.account',
|
||||||
'cic_eth.callbacks.noop',
|
'cic_eth.callbacks.noop',
|
||||||
'cic_eth.callbacks.http',
|
'cic_eth.callbacks.http',
|
||||||
'tests.mock.filter',
|
'cic_eth.pytest.mock.filter',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +13,9 @@ logg = logging.getLogger(__name__)
|
|||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
def load_config():
|
def load_config():
|
||||||
config_dir = os.path.join(root_dir, 'config/test')
|
config_dir = os.environ.get('CONFINI_DIR')
|
||||||
|
if config_dir == None:
|
||||||
|
config_dir = os.path.join(root_dir, 'config/test')
|
||||||
conf = confini.Config(config_dir, 'CICTEST')
|
conf = confini.Config(config_dir, 'CICTEST')
|
||||||
conf.process()
|
conf.process()
|
||||||
logg.debug('config {}'.format(conf))
|
logg.debug('config {}'.format(conf))
|
||||||
|
19
apps/cic-eth/cic_eth/pytest/fixtures_token.py
Normal file
19
apps/cic-eth/cic_eth/pytest/fixtures_token.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# external imports
|
||||||
|
import pytest
|
||||||
|
from eth_erc20 import ERC20
|
||||||
|
|
||||||
|
# TODO: missing dep fixture includes
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def foo_token_symbol(
|
||||||
|
default_chain_spec,
|
||||||
|
foo_token,
|
||||||
|
eth_rpc,
|
||||||
|
contract_roles,
|
||||||
|
):
|
||||||
|
|
||||||
|
c = ERC20(default_chain_spec)
|
||||||
|
o = c.symbol(foo_token, sender_address=contract_roles['CONTRACT_DEPLOYER'])
|
||||||
|
r = eth_rpc.do(o)
|
||||||
|
return c.parse_symbol(r)
|
@ -7,6 +7,8 @@ import tempfile
|
|||||||
import re
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
import websocket
|
import websocket
|
||||||
|
import stat
|
||||||
|
import importlib
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
import celery
|
import celery
|
||||||
@ -68,6 +70,8 @@ from cic_eth.task import BaseTask
|
|||||||
logging.basicConfig(level=logging.WARNING)
|
logging.basicConfig(level=logging.WARNING)
|
||||||
logg = logging.getLogger()
|
logg = logging.getLogger()
|
||||||
|
|
||||||
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
config_dir = os.path.join('/usr/local/etc/cic-eth')
|
config_dir = os.path.join('/usr/local/etc/cic-eth')
|
||||||
|
|
||||||
argparser = argparse.ArgumentParser()
|
argparser = argparse.ArgumentParser()
|
||||||
@ -79,6 +83,8 @@ argparser.add_argument('--default-token-symbol', dest='default_token_symbol', ty
|
|||||||
argparser.add_argument('--trace-queue-status', default=None, dest='trace_queue_status', action='store_true', help='set to perist all queue entry status changes to storage')
|
argparser.add_argument('--trace-queue-status', default=None, dest='trace_queue_status', action='store_true', help='set to perist all queue entry status changes to storage')
|
||||||
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, help='chain spec')
|
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, help='chain spec')
|
||||||
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
|
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
|
||||||
|
argparser.add_argument('--aux-all', action='store_true', help='include tasks from all submodules from the aux module path')
|
||||||
|
argparser.add_argument('--aux', action='append', type=str, default=[], help='add single submodule from the aux module path')
|
||||||
argparser.add_argument('-v', action='store_true', help='be verbose')
|
argparser.add_argument('-v', action='store_true', help='be verbose')
|
||||||
argparser.add_argument('-vv', action='store_true', help='be more verbose')
|
argparser.add_argument('-vv', action='store_true', help='be more verbose')
|
||||||
args = argparser.parse_args()
|
args = argparser.parse_args()
|
||||||
@ -109,6 +115,8 @@ if len(health_modules) != 0:
|
|||||||
health_modules = health_modules.split(',')
|
health_modules = health_modules.split(',')
|
||||||
logg.debug('health mods {}'.format(health_modules))
|
logg.debug('health mods {}'.format(health_modules))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# connect to database
|
# connect to database
|
||||||
dsn = dsn_from_config(config)
|
dsn = dsn_from_config(config)
|
||||||
SessionBase.connect(dsn, pool_size=int(config.get('DATABASE_POOL_SIZE')), debug=config.true('DATABASE_DEBUG'))
|
SessionBase.connect(dsn, pool_size=int(config.get('DATABASE_POOL_SIZE')), debug=config.true('DATABASE_DEBUG'))
|
||||||
@ -167,6 +175,84 @@ Otx.tracing = config.true('TASKS_TRACE_QUEUE_STATUS')
|
|||||||
# raise RuntimeError()
|
# raise RuntimeError()
|
||||||
liveness.linux.load(health_modules, rundir=config.get('CIC_RUN_DIR'), config=config, unit='cic-eth-tasker')
|
liveness.linux.load(health_modules, rundir=config.get('CIC_RUN_DIR'), config=config, unit='cic-eth-tasker')
|
||||||
|
|
||||||
|
rpc = RPCConnection.connect(chain_spec, 'default')
|
||||||
|
try:
|
||||||
|
registry = connect_registry(rpc, chain_spec, config.get('CIC_REGISTRY_ADDRESS'))
|
||||||
|
except UnknownContractError as e:
|
||||||
|
logg.exception('Registry contract connection failed for {}: {}'.format(config.get('CIC_REGISTRY_ADDRESS'), e))
|
||||||
|
sys.exit(1)
|
||||||
|
logg.info('connected contract registry {}'.format(config.get('CIC_REGISTRY_ADDRESS')))
|
||||||
|
|
||||||
|
trusted_addresses_src = config.get('CIC_TRUST_ADDRESS')
|
||||||
|
if trusted_addresses_src == None:
|
||||||
|
logg.critical('At least one trusted address must be declared in CIC_TRUST_ADDRESS')
|
||||||
|
sys.exit(1)
|
||||||
|
trusted_addresses = trusted_addresses_src.split(',')
|
||||||
|
for address in trusted_addresses:
|
||||||
|
logg.info('using trusted address {}'.format(address))
|
||||||
|
|
||||||
|
connect_declarator(rpc, chain_spec, trusted_addresses)
|
||||||
|
connect_token_registry(rpc, chain_spec)
|
||||||
|
|
||||||
|
# detect aux
|
||||||
|
# TODO: move to separate file
|
||||||
|
#aux_dir = os.path.join(script_dir, '..', '..', 'aux')
|
||||||
|
aux = []
|
||||||
|
if args.aux_all:
|
||||||
|
if len(args.aux) > 0:
|
||||||
|
logg.warning('--aux-all is set so --aux will have no effect')
|
||||||
|
for p in sys.path:
|
||||||
|
logg.debug('checking for aux modules in {}'.format(p))
|
||||||
|
aux_dir = os.path.join(p, 'cic_eth_aux')
|
||||||
|
try:
|
||||||
|
d = os.listdir(aux_dir)
|
||||||
|
except FileNotFoundError:
|
||||||
|
logg.debug('no aux module found in {}'.format(aux_dir))
|
||||||
|
continue
|
||||||
|
for v in d:
|
||||||
|
if v[:1] == '.':
|
||||||
|
logg.debug('dotfile, skip {}'.format(v))
|
||||||
|
continue
|
||||||
|
aux_mod_path = os.path.join(aux_dir, v)
|
||||||
|
st = os.stat(aux_mod_path)
|
||||||
|
if not stat.S_ISDIR(st.st_mode):
|
||||||
|
logg.debug('not a dir, skip {}'.format(v))
|
||||||
|
continue
|
||||||
|
aux_mod_file = os.path.join(aux_dir, v,'__init__.py')
|
||||||
|
try:
|
||||||
|
st = os.stat(aux_mod_file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
logg.debug('__init__.py not found, skip {}'.format(v))
|
||||||
|
continue
|
||||||
|
aux.append(v)
|
||||||
|
logg.debug('found module {} in {}'.format(v, aux_dir))
|
||||||
|
|
||||||
|
elif len(args.aux) > 0:
|
||||||
|
for p in sys.path:
|
||||||
|
v_found = None
|
||||||
|
for v in args.aux:
|
||||||
|
aux_dir = os.path.join(p, 'cic_eth_aux')
|
||||||
|
aux_mod_file = os.path.join(aux_dir, v, '__init__.py')
|
||||||
|
try:
|
||||||
|
st = os.stat(aux_mod_file)
|
||||||
|
v_found = v
|
||||||
|
except FileNotFoundError:
|
||||||
|
logg.debug('cannot find explicity requested aux module {} in path {}'.format(v, aux_dir))
|
||||||
|
continue
|
||||||
|
if v_found == None:
|
||||||
|
logg.critical('excplicity requested aux module {} not found in any path'.format(v))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
logg.info('aux module {} found in path {}'.format(v, aux_dir))
|
||||||
|
aux.append(v)
|
||||||
|
|
||||||
|
for v in aux:
|
||||||
|
mname = 'cic_eth_aux.' + v
|
||||||
|
mod = importlib.import_module(mname)
|
||||||
|
mod.aux_setup(rpc, config)
|
||||||
|
logg.info('loaded aux module {}'.format(mname))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argv = ['worker']
|
argv = ['worker']
|
||||||
if args.vv:
|
if args.vv:
|
||||||
@ -189,24 +275,6 @@ def main():
|
|||||||
|
|
||||||
rpc = RPCConnection.connect(chain_spec, 'default')
|
rpc = RPCConnection.connect(chain_spec, 'default')
|
||||||
|
|
||||||
try:
|
|
||||||
registry = connect_registry(rpc, chain_spec, config.get('CIC_REGISTRY_ADDRESS'))
|
|
||||||
except UnknownContractError as e:
|
|
||||||
logg.exception('Registry contract connection failed for {}: {}'.format(config.get('CIC_REGISTRY_ADDRESS'), e))
|
|
||||||
sys.exit(1)
|
|
||||||
logg.info('connected contract registry {}'.format(config.get('CIC_REGISTRY_ADDRESS')))
|
|
||||||
|
|
||||||
trusted_addresses_src = config.get('CIC_TRUST_ADDRESS')
|
|
||||||
if trusted_addresses_src == None:
|
|
||||||
logg.critical('At least one trusted address must be declared in CIC_TRUST_ADDRESS')
|
|
||||||
sys.exit(1)
|
|
||||||
trusted_addresses = trusted_addresses_src.split(',')
|
|
||||||
for address in trusted_addresses:
|
|
||||||
logg.info('using trusted address {}'.format(address))
|
|
||||||
|
|
||||||
connect_declarator(rpc, chain_spec, trusted_addresses)
|
|
||||||
connect_token_registry(rpc, chain_spec)
|
|
||||||
|
|
||||||
BaseTask.default_token_symbol = config.get('CIC_DEFAULT_TOKEN_SYMBOL')
|
BaseTask.default_token_symbol = config.get('CIC_DEFAULT_TOKEN_SYMBOL')
|
||||||
BaseTask.default_token_address = registry.by_name(BaseTask.default_token_symbol)
|
BaseTask.default_token_address = registry.by_name(BaseTask.default_token_symbol)
|
||||||
default_token = ERC20Token(chain_spec, rpc, BaseTask.default_token_address)
|
default_token = ERC20Token(chain_spec, rpc, BaseTask.default_token_address)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# import
|
# import
|
||||||
import time
|
import time
|
||||||
import requests
|
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@ -76,7 +75,7 @@ class CriticalSQLAlchemyTask(CriticalTask):
|
|||||||
|
|
||||||
class CriticalWeb3Task(CriticalTask):
|
class CriticalWeb3Task(CriticalTask):
|
||||||
autoretry_for = (
|
autoretry_for = (
|
||||||
requests.exceptions.ConnectionError,
|
ConnectionError,
|
||||||
)
|
)
|
||||||
safe_gas_threshold_amount = 2000000000 * 60000 * 3
|
safe_gas_threshold_amount = 2000000000 * 60000 * 3
|
||||||
safe_gas_refill_amount = safe_gas_threshold_amount * 5
|
safe_gas_refill_amount = safe_gas_threshold_amount * 5
|
||||||
@ -86,7 +85,7 @@ class CriticalSQLAlchemyAndWeb3Task(CriticalTask):
|
|||||||
autoretry_for = (
|
autoretry_for = (
|
||||||
sqlalchemy.exc.DatabaseError,
|
sqlalchemy.exc.DatabaseError,
|
||||||
sqlalchemy.exc.TimeoutError,
|
sqlalchemy.exc.TimeoutError,
|
||||||
requests.exceptions.ConnectionError,
|
ConnectionError,
|
||||||
sqlalchemy.exc.ResourceClosedError,
|
sqlalchemy.exc.ResourceClosedError,
|
||||||
)
|
)
|
||||||
safe_gas_threshold_amount = 2000000000 * 60000 * 3
|
safe_gas_threshold_amount = 2000000000 * 60000 * 3
|
||||||
@ -102,7 +101,7 @@ class CriticalSQLAlchemyAndSignerTask(CriticalTask):
|
|||||||
|
|
||||||
class CriticalWeb3AndSignerTask(CriticalTask):
|
class CriticalWeb3AndSignerTask(CriticalTask):
|
||||||
autoretry_for = (
|
autoretry_for = (
|
||||||
requests.exceptions.ConnectionError,
|
ConnectionError,
|
||||||
)
|
)
|
||||||
safe_gas_threshold_amount = 2000000000 * 60000 * 3
|
safe_gas_threshold_amount = 2000000000 * 60000 * 3
|
||||||
safe_gas_refill_amount = safe_gas_threshold_amount * 5
|
safe_gas_refill_amount = safe_gas_threshold_amount * 5
|
||||||
|
@ -10,7 +10,7 @@ version = (
|
|||||||
0,
|
0,
|
||||||
12,
|
12,
|
||||||
0,
|
0,
|
||||||
'alpha.1',
|
'alpha.2',
|
||||||
)
|
)
|
||||||
|
|
||||||
version_object = semver.VersionInfo(
|
version_object = semver.VersionInfo(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
FROM python:3.8.6-slim-buster as compile
|
FROM python:3.8.6-slim-buster as compile
|
||||||
|
|
||||||
WORKDIR /usr/src/cic-eth
|
WORKDIR /usr/src
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
|
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
|
||||||
@ -13,10 +13,17 @@ ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages
|
|||||||
RUN /usr/local/bin/python -m pip install --upgrade pip
|
RUN /usr/local/bin/python -m pip install --upgrade pip
|
||||||
RUN pip install semver
|
RUN pip install semver
|
||||||
|
|
||||||
COPY cic-eth/ .
|
COPY cic-eth-aux/ ./cic-eth-aux/
|
||||||
|
WORKDIR /usr/src/cic-eth-aux/erc20-demurrage-token
|
||||||
RUN pip install --extra-index-url $GITLAB_PYTHON_REGISTRY \
|
RUN pip install --extra-index-url $GITLAB_PYTHON_REGISTRY \
|
||||||
--extra-index-url $EXTRA_INDEX_URL .
|
--extra-index-url $EXTRA_INDEX_URL .
|
||||||
|
|
||||||
|
WORKDIR /usr/src/cic-eth
|
||||||
|
|
||||||
|
COPY cic-eth/ .
|
||||||
|
RUN pip install --extra-index-url $GITLAB_PYTHON_REGISTRY \
|
||||||
|
--extra-index-url $EXTRA_INDEX_URL .[services]
|
||||||
|
|
||||||
# --- TEST IMAGE ---
|
# --- TEST IMAGE ---
|
||||||
FROM python:3.8.6-slim-buster as test
|
FROM python:3.8.6-slim-buster as test
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ set -e
|
|||||||
echo "!!! starting signer"
|
echo "!!! starting signer"
|
||||||
python /usr/local/bin/crypto-dev-daemon -c /usr/local/etc/crypto-dev-signer -vv 2> /tmp/signer.log &
|
python /usr/local/bin/crypto-dev-daemon -c /usr/local/etc/crypto-dev-signer -vv 2> /tmp/signer.log &
|
||||||
|
|
||||||
echo "!!! starting tracker"
|
echo "!!! starting taskerd"
|
||||||
/usr/local/bin/cic-eth-taskerd $@
|
/usr/local/bin/cic-eth-taskerd $@
|
||||||
|
|
||||||
# thanks! https://docs.docker.com/config/containers/multi-service_container/
|
# thanks! https://docs.docker.com/config/containers/multi-service_container/
|
||||||
|
@ -5,3 +5,10 @@ confini~=0.3.6rc4
|
|||||||
redis==3.5.3
|
redis==3.5.3
|
||||||
hexathon~=0.0.1a7
|
hexathon~=0.0.1a7
|
||||||
pycryptodome==3.10.1
|
pycryptodome==3.10.1
|
||||||
|
liveness~=0.0.1a7
|
||||||
|
eth-address-index~=0.1.2a1
|
||||||
|
eth-accounts-index~=0.0.12a1
|
||||||
|
cic-eth-registry~=0.5.6a1
|
||||||
|
erc20-faucet~=0.2.2a1
|
||||||
|
sarafu-faucet~=0.0.4a1
|
||||||
|
moolb~=0.1.1b2
|
||||||
|
@ -39,6 +39,9 @@ packages =
|
|||||||
cic_eth.callbacks
|
cic_eth.callbacks
|
||||||
cic_eth.sync
|
cic_eth.sync
|
||||||
cic_eth.check
|
cic_eth.check
|
||||||
|
# should be concealed behind extras "test" if possible (but its not unfortunately)
|
||||||
|
cic_eth.pytest
|
||||||
|
cic_eth.pytest.mock
|
||||||
scripts =
|
scripts =
|
||||||
./scripts/migrate.py
|
./scripts/migrate.py
|
||||||
|
|
||||||
|
@ -48,4 +48,4 @@ setup(
|
|||||||
'admin_api': admin_requirements,
|
'admin_api': admin_requirements,
|
||||||
'services': services_requirements,
|
'services': services_requirements,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ import logging
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
|
import pytest
|
||||||
from eth_erc20 import ERC20
|
from eth_erc20 import ERC20
|
||||||
import redis
|
import redis
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ from cic_eth.pytest.fixtures_celery import *
|
|||||||
from cic_eth.pytest.fixtures_database import *
|
from cic_eth.pytest.fixtures_database import *
|
||||||
from cic_eth.pytest.fixtures_role import *
|
from cic_eth.pytest.fixtures_role import *
|
||||||
from cic_eth.pytest.fixtures_contract import *
|
from cic_eth.pytest.fixtures_contract import *
|
||||||
|
from cic_eth.pytest.fixtures_token import *
|
||||||
from chainlib.eth.pytest import *
|
from chainlib.eth.pytest import *
|
||||||
from eth_contract_registry.pytest import *
|
from eth_contract_registry.pytest import *
|
||||||
from cic_eth_registry.pytest.fixtures_contracts import *
|
from cic_eth_registry.pytest.fixtures_contracts import *
|
||||||
@ -37,20 +39,6 @@ def api(
|
|||||||
return Api(chain_str, queue=None, callback_param='foo')
|
return Api(chain_str, queue=None, callback_param='foo')
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def foo_token_symbol(
|
|
||||||
default_chain_spec,
|
|
||||||
foo_token,
|
|
||||||
eth_rpc,
|
|
||||||
contract_roles,
|
|
||||||
):
|
|
||||||
|
|
||||||
c = ERC20(default_chain_spec)
|
|
||||||
o = c.symbol(foo_token, sender_address=contract_roles['CONTRACT_DEPLOYER'])
|
|
||||||
r = eth_rpc.do(o)
|
|
||||||
return c.parse_symbol(r)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope='function')
|
||||||
def default_token(
|
def default_token(
|
||||||
foo_token,
|
foo_token,
|
||||||
|
@ -15,7 +15,7 @@ from cic_eth.db.models.nonce import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
# test imports
|
# test imports
|
||||||
from tests.mock.filter import (
|
from cic_eth.pytest.mock.filter import (
|
||||||
block_filter,
|
block_filter,
|
||||||
tx_filter,
|
tx_filter,
|
||||||
)
|
)
|
||||||
@ -110,7 +110,7 @@ def test_list_tx(
|
|||||||
logg.debug('r {}'.format(r))
|
logg.debug('r {}'.format(r))
|
||||||
|
|
||||||
# test the api
|
# test the api
|
||||||
t = api.list(agent_roles['ALICE'], external_task='tests.mock.filter.filter')
|
t = api.list(agent_roles['ALICE'], external_task='cic_eth.pytest.mock.filter.filter')
|
||||||
r = t.get_leaf()
|
r = t.get_leaf()
|
||||||
assert t.successful()
|
assert t.successful()
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
cic_base[full]==0.1.3a4+build.ce68c833
|
cic_base[full]==0.1.3a4+build.ce68c833
|
||||||
sarafu-faucet~=0.0.4a1
|
sarafu-faucet~=0.0.4a1
|
||||||
cic-eth==0.11.1a3+build.a3c49324
|
cic-eth[tools]==0.12.0a2
|
||||||
eth-erc20~=0.0.10a3
|
eth-erc20~=0.0.10a3
|
||||||
erc20-demurrage-token==0.0.2a3
|
erc20-demurrage-token==0.0.2a3
|
||||||
|
@ -274,7 +274,7 @@ services:
|
|||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
if [[ -f /tmp/cic/config/.env ]]; then source /tmp/cic/config/.env; fi
|
if [[ -f /tmp/cic/config/.env ]]; then source /tmp/cic/config/.env; fi
|
||||||
./start_tasker.sh -q cic-eth -vv
|
./start_tasker.sh --aux-all -q cic-eth -vv
|
||||||
# command: [/bin/sh, "./start_tasker.sh", -q, cic-eth, -vv ]
|
# command: [/bin/sh, "./start_tasker.sh", -q, cic-eth, -vv ]
|
||||||
|
|
||||||
cic-eth-tracker:
|
cic-eth-tracker:
|
||||||
|
Loading…
Reference in New Issue
Block a user