From 453dd470916eb1b28795cd0ed658a5797e1d7345 Mon Sep 17 00:00:00 2001 From: nolash Date: Sun, 21 Mar 2021 12:18:15 +0100 Subject: [PATCH] Rehabilitate api tests --- apps/cic-eth/cic_eth/admin/ctrl.py | 17 +-- apps/cic-eth/cic_eth/api/api_task.py | 52 ++++--- apps/cic-eth/cic_eth/eth/account.py | 5 +- apps/cic-eth/cic_eth/eth/erc20.py | 175 ++---------------------- apps/cic-eth/cic_eth/eth/tx.py | 2 +- apps/cic-eth/tests/fixtures_celery.py | 1 - apps/cic-eth/tests/task/api/test_app.py | 121 ++++++++++++++++ 7 files changed, 171 insertions(+), 202 deletions(-) create mode 100644 apps/cic-eth/tests/task/api/test_app.py diff --git a/apps/cic-eth/cic_eth/admin/ctrl.py b/apps/cic-eth/cic_eth/admin/ctrl.py index 0edeab0b..c3815d3b 100644 --- a/apps/cic-eth/cic_eth/admin/ctrl.py +++ b/apps/cic-eth/cic_eth/admin/ctrl.py @@ -4,7 +4,8 @@ import logging # third-party imports import celery -from cic_registry import zero_address +from chainlib.eth.constant import ZERO_ADDRESS +from chainlib.chain import ChainSpec # local imports from cic_eth.db.enum import LockEnum @@ -19,7 +20,7 @@ celery_app = celery.current_app logg = logging.getLogger() @celery_app.task(base=CriticalSQLAlchemyTask) -def lock(chained_input, chain_spec_dict, address=zero_address, flags=LockEnum.ALL, tx_hash=None): +def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.ALL, tx_hash=None): """Task wrapper to set arbitrary locks :param chain_str: Chain spec string representation @@ -38,7 +39,7 @@ def lock(chained_input, chain_spec_dict, address=zero_address, flags=LockEnum.AL @celery_app.task(base=CriticalSQLAlchemyTask) -def unlock(chained_input, chain_spec_dict, address=zero_address, flags=LockEnum.ALL): +def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.ALL): """Task wrapper to reset arbitrary locks :param chain_str: Chain spec string representation @@ -57,7 +58,7 @@ def unlock(chained_input, chain_spec_dict, address=zero_address, flags=LockEnum. @celery_app.task(base=CriticalSQLAlchemyTask) -def lock_send(chained_input, chain_spec_dict, address=zero_address, tx_hash=None): +def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None): """Task wrapper to set send lock :param chain_str: Chain spec string representation @@ -74,7 +75,7 @@ def lock_send(chained_input, chain_spec_dict, address=zero_address, tx_hash=None @celery_app.task(base=CriticalSQLAlchemyTask) -def unlock_send(chained_input, chain_spec_dict, address=zero_address): +def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS): """Task wrapper to reset send lock :param chain_str: Chain spec string representation @@ -91,7 +92,7 @@ def unlock_send(chained_input, chain_spec_dict, address=zero_address): @celery_app.task(base=CriticalSQLAlchemyTask) -def lock_queue(chained_input, chain_spec_dict, address=zero_address, tx_hash=None): +def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None): """Task wrapper to set queue direct lock :param chain_str: Chain spec string representation @@ -108,7 +109,7 @@ def lock_queue(chained_input, chain_spec_dict, address=zero_address, tx_hash=Non @celery_app.task(base=CriticalSQLAlchemyTask) -def unlock_queue(chained_input, chain_spec_dict, address=zero_address): +def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS): """Task wrapper to reset queue direct lock :param chain_str: Chain spec string representation @@ -128,7 +129,7 @@ def unlock_queue(chained_input, chain_spec_dict, address=zero_address): def check_lock(chained_input, chain_spec_dict, lock_flags, address=None): chain_str = str(ChainSpec.from_dict(chain_spec_dict)) session = SessionBase.create_session() - r = Lock.check(chain_str, lock_flags, address=zero_address, session=session) + r = Lock.check(chain_str, lock_flags, address=ZERO_ADDRESS, session=session) if address != None: r |= Lock.check(chain_str, lock_flags, address=address, session=session) if r > 0: diff --git a/apps/cic-eth/cic_eth/api/api_task.py b/apps/cic-eth/cic_eth/api/api_task.py index 9013f26f..ad99dae5 100644 --- a/apps/cic-eth/cic_eth/api/api_task.py +++ b/apps/cic-eth/cic_eth/api/api_task.py @@ -8,12 +8,10 @@ import logging # external imports import celery -#from cic_registry.chain import ChainSpec from cic_registry import CICRegistry from chainlib.chain import ChainSpec # local imports -from cic_eth.eth.factory import TxFactory from cic_eth.db.enum import LockEnum app = celery.current_app @@ -87,7 +85,7 @@ class Api: 'cic_eth.admin.ctrl.check_lock', [ [from_token_symbol, to_token_symbol], - self.chain_str, + self.chain_spec.asdict(), LockEnum.QUEUE, from_address, ], @@ -99,7 +97,7 @@ class Api: queue=self.queue, ) s_tokens = celery.signature( - 'cic_eth.eth.token.resolve_tokens_by_symbol', + 'cic_eth.eth.erc20.resolve_tokens_by_symbol', [ self.chain_str, ], @@ -112,7 +110,7 @@ class Api: target_return, minimum_return, to_address, - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -149,7 +147,7 @@ class Api: 'cic_eth.admin.ctrl.check_lock', [ [from_token_symbol, to_token_symbol], - self.chain_str, + self.chain_spec.asdict(), LockEnum.QUEUE, from_address, ], @@ -161,9 +159,9 @@ class Api: queue=self.queue, ) s_tokens = celery.signature( - 'cic_eth.eth.token.resolve_tokens_by_symbol', + 'cic_eth.eth.erc20.resolve_tokens_by_symbol', [ - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -174,7 +172,7 @@ class Api: target_return, minimum_return, from_address, - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -208,7 +206,7 @@ class Api: 'cic_eth.admin.ctrl.check_lock', [ [token_symbol], - self.chain_str, + self.chain_spec.asdict(), LockEnum.QUEUE, from_address, ], @@ -222,19 +220,19 @@ class Api: queue=self.queue, ) s_tokens = celery.signature( - 'cic_eth.eth.token.resolve_tokens_by_symbol', + 'cic_eth.eth.erc20.resolve_tokens_by_symbol', [ - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) s_transfer = celery.signature( - 'cic_eth.eth.token.transfer', + 'cic_eth.eth.erc20.transfer', [ from_address, to_address, value, - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -266,18 +264,18 @@ class Api: logg.warning('balance pointlessly called with no callback url') s_tokens = celery.signature( - 'cic_eth.eth.token.resolve_tokens_by_symbol', + 'cic_eth.eth.erc20.resolve_tokens_by_symbol', [ [token_symbol], - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) s_balance = celery.signature( - 'cic_eth.eth.token.balance', + 'cic_eth.eth.erc20.balance', [ address, - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -293,7 +291,7 @@ class Api: 'cic_eth.queue.balance.balance_incoming', [ address, - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -301,7 +299,7 @@ class Api: 'cic_eth.queue.balance.balance_outgoing', [ address, - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -337,7 +335,7 @@ class Api: 'cic_eth.admin.ctrl.check_lock', [ password, - self.chain_str, + self.chain_spec.asdict(), LockEnum.CREATE, ], queue=self.queue, @@ -345,7 +343,7 @@ class Api: s_account = celery.signature( 'cic_eth.eth.account.create', [ - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -364,7 +362,7 @@ class Api: s_register = celery.signature( 'cic_eth.eth.account.register', [ - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -387,7 +385,7 @@ class Api: 'cic_eth.admin.ctrl.check_lock', [ address, - self.chain_str, + self.chain_spec.asdict(), LockEnum.QUEUE, ], queue=self.queue, @@ -402,7 +400,7 @@ class Api: s_refill = celery.signature( 'cic_eth.eth.tx.refill_gas', [ - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) @@ -445,7 +443,7 @@ class Api: s_brief = celery.signature( 'cic_eth.ext.tx.tx_collate', [ - self.chain_str, + self.chain_spec.asdict(), offset, limit ], @@ -471,7 +469,7 @@ class Api: 'cic_eth.ext.tx.list_tx_by_bloom', [ address, - self.chain_str, + self.chain_spec.asdict(), ], queue=self.queue, ) diff --git a/apps/cic-eth/cic_eth/eth/account.py b/apps/cic-eth/cic_eth/eth/account.py index 15a418ed..b444c998 100644 --- a/apps/cic-eth/cic_eth/eth/account.py +++ b/apps/cic-eth/cic_eth/eth/account.py @@ -92,7 +92,7 @@ def unpack_gift(data): # TODO: Separate out nonce initialization task @celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask) -def create(self, password, chain_str): +def create(self, password, chain_spec_dict): """Creates and stores a new ethereum account in the keystore. The password is passed on to the wallet backend, no encryption is performed in the task worker. @@ -104,8 +104,7 @@ def create(self, password, chain_str): :returns: Ethereum address of newly created account :rtype: str, 0x-hex """ - chain_spec = ChainSpec.from_chain_str(chain_str) - #c = RpcClient(chain_spec) + chain_spec = ChainSpec.from_dict(chain_spec_dict) a = None conn = RPCConnection.connect(chain_spec, 'signer') o = new_account() diff --git a/apps/cic-eth/cic_eth/eth/erc20.py b/apps/cic-eth/cic_eth/eth/erc20.py index 376aa8c6..818e3bff 100644 --- a/apps/cic-eth/cic_eth/eth/erc20.py +++ b/apps/cic-eth/cic_eth/eth/erc20.py @@ -14,6 +14,7 @@ from chainlib.eth.tx import ( TxFormat, unpack, ) +from cic_eth_registry import CICRegistry from cic_eth_registry.erc20 import ERC20Token from hexathon import strip_0x @@ -21,6 +22,7 @@ from hexathon import strip_0x from cic_eth.registry import safe_registry from cic_eth.db.models.tx import TxCache from cic_eth.db.models.base import SessionBase +from cic_eth.db.models.role import AccountRole from cic_eth.eth import RpcClient from cic_eth.error import TokenCountError, PermanentTxError, OutOfGasError, NotLocalTxError from cic_eth.queue.tx import register_tx @@ -40,161 +42,6 @@ from cic_eth.eth.nonce import CustodialTaskNonceOracle celery_app = celery.current_app logg = logging.getLogger() -## TODO: fetch from cic-contracts instead when implemented -#contract_function_signatures = { -# 'transfer': 'a9059cbb', -# 'approve': '095ea7b3', -# 'transferfrom': '23b872dd', -# } -# -# -#class TokenTxFactory(TxFactory): -# """Factory for creating ERC20 token transactions. -# """ -# def approve( -# self, -# token_address, -# spender_address, -# amount, -# chain_spec, -# uuid, -# session=None, -# ): -# """Create an ERC20 "approve" transaction -# -# :param token_address: ERC20 contract address -# :type token_address: str, 0x-hex -# :param spender_address: Address to approve spending for -# :type spender_address: str, 0x-hex -# :param amount: Amount of tokens to approve -# :type amount: int -# :param chain_spec: Chain spec -# :type chain_spec: cic_registry.chain.ChainSpec -# :returns: Unsigned "approve" transaction in standard Ethereum format -# :rtype: dict -# """ -# source_token = self.registry.get_address(chain_spec, token_address) -# source_token_contract = source_token.contract -# tx_approve_buildable = source_token_contract.functions.approve( -# spender_address, -# amount, -# ) -# source_token_gas = source_token.gas('transfer') -# -# tx_approve = tx_approve_buildable.buildTransaction({ -# 'from': self.address, -# 'gas': source_token_gas, -# 'gasPrice': self.gas_price, -# 'chainId': chain_spec.chain_id(), -# 'nonce': self.next_nonce(uuid, session=session), -# }) -# return tx_approve -# -# -# def transfer( -# self, -# token_address, -# Receiver_address, -# value, -# chain_spec, -# uuid, -# session=None, -# ): -# """Create an ERC20 "transfer" transaction -# -# :param token_address: ERC20 contract address -# :type token_address: str, 0x-hex -# :param receiver_address: Address to send tokens to -# :type receiver_address: str, 0x-hex -# :param amount: Amount of tokens to send -# :type amount: int -# :param chain_spec: Chain spec -# :type chain_spec: cic_registry.chain.ChainSpec -# :returns: Unsigned "transfer" transaction in standard Ethereum format -# :rtype: dict -# """ -# source_token = self.registry.get_address(chain_spec, token_address) -# source_token_contract = source_token.contract -# transfer_buildable = source_token_contract.functions.transfer( -# receiver_address, -# value, -# ) -# source_token_gas = source_token.gas('transfer') -# -# tx_transfer = transfer_buildable.buildTransaction( -# { -# 'from': self.address, -# 'gas': source_token_gas, -# 'gasPrice': self.gas_price, -# 'chainId': chain_spec.chain_id(), -# 'nonce': self.next_nonce(uuid, session=session), -# }) -# return tx_transfer - - -#def unpack_transfer(data): -# """Verifies that a transaction is an "ERC20.transfer" transaction, and extracts call parameters from it. -# -# :param data: Raw input data from Ethereum transaction. -# :type data: str, 0x-hex -# :raises ValueError: Function signature does not match AccountRegister.add -# :returns: Parsed parameters -# :rtype: dict -# """ -# data = strip_0x(data) -# f = data[:8] -# if f != contract_function_signatures['transfer']: -# raise ValueError('Invalid transfer data ({})'.format(f)) -# -# d = data[8:] -# return { -# 'to': web3.Web3.toChecksumAddress('0x' + d[64-40:64]), -# 'amount': int(d[64:], 16) -# } - - -#def unpack_transferfrom(data): -# """Verifies that a transaction is an "ERC20.transferFrom" transaction, and extracts call parameters from it. -# -# :param data: Raw input data from Ethereum transaction. -# :type data: str, 0x-hex -# :raises ValueError: Function signature does not match AccountRegister.add -# :returns: Parsed parameters -# :rtype: dict -# """ -# data = strip_0x(data) -# f = data[:8] -# if f != contract_function_signatures['transferfrom']: -# raise ValueError('Invalid transferFrom data ({})'.format(f)) -# -# d = data[8:] -# return { -# 'from': web3.Web3.toChecksumAddress('0x' + d[64-40:64]), -# 'to': web3.Web3.toChecksumAddress('0x' + d[128-40:128]), -# 'amount': int(d[128:], 16) -# } -# -# -#def unpack_approve(data): -# """Verifies that a transaction is an "ERC20.approve" transaction, and extracts call parameters from it. -# -# :param data: Raw input data from Ethereum transaction. -# :type data: str, 0x-hex -# :raises ValueError: Function signature does not match AccountRegister.add -# :returns: Parsed parameters -# :rtype: dict -# """ -# data = strip_0x(data) -# f = data[:8] -# if f != contract_function_signatures['approve']: -# raise ValueError('Invalid approval data ({})'.format(f)) -# -# d = data[8:] -# return { -# 'to': web3.Web3.toChecksumAddress('0x' + d[64-40:64]), -# 'amount': int(d[64:], 16) -# } - @celery_app.task(base=CriticalWeb3Task) def balance(tokens, holder_address, chain_spec_dict): @@ -341,8 +188,8 @@ def approve(self, tokens, holder_address, spender_address, value, chain_spec_dic return tx_hash_hex -@celery_app.task(base=CriticalWeb3Task) -def resolve_tokens_by_symbol(token_symbols, chain_str): +@celery_app.task(bind=True, base=CriticalWeb3Task) +def resolve_tokens_by_symbol(self, token_symbols, chain_spec_dict): """Returns contract addresses of an array of ERC20 token symbols :param token_symbols: Token symbols to resolve @@ -354,13 +201,17 @@ def resolve_tokens_by_symbol(token_symbols, chain_str): :rtype: list of str, 0x-hex """ tokens = [] - chain_spec = ChainSpec.from_chain_str(chain_str) - c = RpcClient(chain_spec) - registry = safe_registry(c.w3) + chain_spec = ChainSpec.from_dict(chain_spec_dict) + rpc = RPCConnection.connect(chain_spec, 'default') + registry = CICRegistry(chain_spec, rpc) + session = self.create_session() + sender_address = AccountRole.get_address('DEFAULT', session) + session.close() for token_symbol in token_symbols: - token = registry.get_token(chain_spec, token_symbol) + token_address = registry.by_name(token_symbol, sender_address=sender_address) + logg.debug('token {}'.format(token_address)) tokens.append({ - 'address': token.address(), + 'address': token_address, 'converters': [], }) return tokens diff --git a/apps/cic-eth/cic_eth/eth/tx.py b/apps/cic-eth/cic_eth/eth/tx.py index 663f2259..95edef92 100644 --- a/apps/cic-eth/cic_eth/eth/tx.py +++ b/apps/cic-eth/cic_eth/eth/tx.py @@ -352,7 +352,7 @@ def refill_gas(self, recipient_address, chain_spec_dict): logg.debug('tx send gas amount {} from provider {} to {}'.format(refill_amount, gas_provider, recipient_address)) (tx_hash_hex, tx_signed_raw_hex) = c.create(gas_provider, recipient_address, refill_amount, tx_format=TxFormat.RLP_SIGNED) logg.debug('adding queue refill gas tx {}'.format(tx_hash_hex)) - cache_task = 'cic_eth.eth.tx.otx_cache_parse_tx' + cache_task = 'cic_eth.eth.tx.cache_gas_data' register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=cache_task, session=session) # add transaction to send queue diff --git a/apps/cic-eth/tests/fixtures_celery.py b/apps/cic-eth/tests/fixtures_celery.py index d87cd13b..257c3f30 100644 --- a/apps/cic-eth/tests/fixtures_celery.py +++ b/apps/cic-eth/tests/fixtures_celery.py @@ -11,7 +11,6 @@ logg = logging.getLogger() # celery fixtures @pytest.fixture(scope='session') def celery_includes(): - logg.debug('foooooooooooo ') return [ # 'cic_eth.eth.bancor', 'cic_eth.eth.erc20', diff --git a/apps/cic-eth/tests/task/api/test_app.py b/apps/cic-eth/tests/task/api/test_app.py new file mode 100644 index 00000000..2c8b9def --- /dev/null +++ b/apps/cic-eth/tests/task/api/test_app.py @@ -0,0 +1,121 @@ +# standard imports +import os +import logging +import time + +# external imports +import pytest +import celery +from cic_eth_registry.erc20 import ERC20Token +from chainlib.chain import ChainSpec + +# local imports +from cic_eth.api import Api + +logg = logging.getLogger(__name__) + + +def test_account_api( + default_chain_spec, + init_database, + init_eth_rpc, + account_registry, + custodial_roles, + celery_session_worker, + ): + api = Api(str(default_chain_spec), callback_param='accounts', callback_task='cic_eth.callbacks.noop.noop', queue=None) + t = api.create_account('', register=False) + t.get_leaf() + assert t.successful() + + +def test_transfer_api( + default_chain_spec, + eth_rpc, + init_database, + foo_token, + custodial_roles, + agent_roles, + cic_registry, + token_registry_load, + celery_session_worker, + ): + + #token = CICRegistry.get_address(default_chain_spec, bancor_tokens[0]) + foo_token_cache = ERC20Token(eth_rpc, foo_token) + + api = Api(str(default_chain_spec), callback_param='transfer', callback_task='cic_eth.callbacks.noop.noop', queue=None) + t = api.transfer(custodial_roles['FOO_TOKEN_GIFTER'], agent_roles['ALICE'], 1024, foo_token_cache.symbol) + t.get_leaf() + assert t.successful() + + +@pytest.mark.skip() +def test_convert_api( + default_chain_spec, + init_w3, + cic_registry, + init_database, + foo_token, + bar_token, + celery_session_worker, + ): + + token_alice = CICRegistry.get_address(default_chain_spec, bancor_tokens[0]) + token_bob = CICRegistry.get_address(default_chain_spec, bancor_tokens[1]) + + api = Api(str(default_chain_spec), callback_param='convert', callback_task='cic_eth.callbacks.noop.noop', queue=None) + t = api.convert(custodial_roles['FOO_TOKEN_GIFTER'], 110, 100, foo_token_cache.symbol, bar_token_cache.symbol) + t.get_leaf() + assert t.successful() + + +@pytest.mark.skip() +def test_convert_transfer_api( + default_chain_spec, + init_w3, + cic_registry, + init_database, + bancor_registry, + bancor_tokens, + celery_session_worker, + ): + + token_alice = CICRegistry.get_address(default_chain_spec, bancor_tokens[0]) + token_bob = CICRegistry.get_address(default_chain_spec, bancor_tokens[1]) + + api = Api(str(default_chain_spec), callback_param='convert_transfer', callback_task='cic_eth.callbacks.noop.noop', queue=None) + t = api.convert_transfer(init_w3.eth.accounts[2], init_w3.eth.accounts[4], 110, 100, token_alice.symbol(), token_bob.symbol()) + t.get() + for r in t.collect(): + print(r) + assert t.successful() + + +def test_refill_gas( + default_chain_spec, + init_database, + eth_empty_accounts, + init_eth_rpc, + custodial_roles, + celery_session_worker, + ): + + api = Api(str(default_chain_spec), callback_param='refill_gas', callback_task='cic_eth.callbacks.noop.noop', queue=None) + t = api.refill_gas(eth_empty_accounts[0]) + t.get() + for r in t.collect(): + print(r) + assert t.successful() + + +def test_ping( + default_chain_spec, + celery_session_worker, + ): + api = Api(str(default_chain_spec), callback_param='ping', callback_task='cic_eth.callbacks.noop.noop', queue=None) + t = api.ping('pong') + t.get() + for r in t.collect(): + print(r) + assert t.successful()