cic-eth: Introduce transfer authorization contract

This commit is contained in:
Louis Holbrook 2021-03-04 15:06:14 +00:00 committed by Philip Wafula
parent 1e9bf6b4d3
commit 8240e58c0a
29 changed files with 312 additions and 366 deletions

View File

@ -1,12 +1,25 @@
# standard imports
import datetime import datetime
# external imports
import celery import celery
# local imports
from cic_eth.db.models.debug import Debug
from cic_eth.db.models.base import SessionBase
from cic_eth.task import CriticalSQLAlchemyTask
celery_app = celery.current_app celery_app = celery.current_app
@celery_app.task() @celery_app.task(base=CriticalSQLAlchemyTask)
def out_tmp(tag, txt): def alert(chained_input, tag, txt):
f = open('/tmp/err.{}.txt'.format(tag), "w") session = SessionBase.create_session()
f.write(txt)
f.close() o = Debug(tag, txt)
session.add(o)
session.commit()
session.close()
return chained_input

View File

@ -489,8 +489,9 @@ class Api:
], ],
queue=self.queue, queue=self.queue,
) )
s_local.link(s_brief)
if self.callback_param != None: if self.callback_param != None:
s_assemble.link(self.callback_success).on_error(self.callback_error) s_brief.link(self.callback_success).on_error(self.callback_error)
t = None t = None
if external_task != None: if external_task != None:
@ -515,11 +516,10 @@ class Api:
c = celery.chain(s_external_get, s_external_process) c = celery.chain(s_external_get, s_external_process)
t = celery.chord([s_local, c])(s_brief) t = celery.chord([s_local, c])(s_brief)
else: else:
t = s_local.apply_sync() t = s_local.apply_async(queue=self.queue)
return t return t
def ping(self, r): def ping(self, r):
"""A noop callback ping for testing purposes. """A noop callback ping for testing purposes.

View File

@ -0,0 +1,32 @@
"""debug output
Revision ID: f738d9962fdf
Revises: ec40ac0974c1
Create Date: 2021-03-04 08:32:43.281214
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f738d9962fdf'
down_revision = 'ec40ac0974c1'
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
'debug',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('tag', sa.String, nullable=False),
sa.Column('description', sa.String, nullable=False),
sa.Column('date_created', sa.DateTime, nullable=False),
)
pass
def downgrade():
op.drop_table('debug')
pass

View File

@ -0,0 +1,32 @@
"""debug output
Revision ID: f738d9962fdf
Revises: ec40ac0974c1
Create Date: 2021-03-04 08:32:43.281214
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f738d9962fdf'
down_revision = 'ec40ac0974c1'
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
'debug',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('tag', sa.String, nullable=False),
sa.Column('description', sa.String, nullable=False),
sa.Column('date_created', sa.DateTime, nullable=False),
)
pass
def downgrade():
op.drop_table('debug')
pass

View File

@ -0,0 +1,23 @@
# standard imports
import datetime
import logging
# external imports
from sqlalchemy import Column, String, DateTime
# local imports
from .base import SessionBase
class Debug(SessionBase):
__tablename__ = 'debug'
date_created = Column(DateTime, default=datetime.datetime.utcnow)
tag = Column(String)
description = Column(String)
def __init__(self, tag, description):
self.tag = tag
self.description = description

View File

@ -88,7 +88,7 @@ class Nonce(SessionBase):
#session.execute('UNLOCK TABLE nonce') #session.execute('UNLOCK TABLE nonce')
#conn.close() #conn.close()
session.commit() session.commit()
session.commit() # session.commit()
SessionBase.release_session(session) SessionBase.release_session(session)
return nonce return nonce

View File

@ -2,7 +2,7 @@
import datetime import datetime
import logging import logging
# third-party imports # external imports
from sqlalchemy import Column, Enum, String, Integer, DateTime, Text, or_, ForeignKey from sqlalchemy import Column, Enum, String, Integer, DateTime, Text, or_, ForeignKey
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method

View File

@ -1,194 +0,0 @@
# standard imports
import logging
# third-party imports
import web3
import celery
from erc20_approval_escrow import TransferApproval
from cic_registry import CICRegistry
from cic_registry.chain import ChainSpec
# local imports
from cic_eth.db.models.tx import TxCache
from cic_eth.db.models.base import SessionBase
from cic_eth.eth import RpcClient
from cic_eth.eth.factory import TxFactory
from cic_eth.eth.task import sign_and_register_tx
from cic_eth.eth.util import unpack_signed_raw_tx
from cic_eth.eth.task import create_check_gas_and_send_task
from cic_eth.error import TokenCountError
celery_app = celery.current_app
logg = logging.getLogger()
contract_function_signatures = {
'request': 'b0addede',
}
class TransferRequestTxFactory(TxFactory):
"""Factory for creating Transfer request transactions using the TransferApproval contract backend
"""
def request(
self,
token_address,
beneficiary_address,
amount,
chain_spec,
):
"""Create a new TransferApproval.request transaction
:param token_address: Token to create transfer request for
:type token_address: str, 0x-hex
:param beneficiary_address: Beneficiary of token transfer
:type beneficiary_address: str, 0x-hex
:param amount: Amount of tokens to transfer
:type amount: number
:param chain_spec: Chain spec
:type chain_spec: cic_registry.chain.ChainSpec
:returns: Transaction in standard Ethereum format
:rtype: dict
"""
transfer_approval = CICRegistry.get_contract(chain_spec, 'TransferApproval', 'TransferAuthorization')
fn = transfer_approval.function('createRequest')
tx_approval_buildable = fn(beneficiary_address, token_address, amount)
transfer_approval_gas = transfer_approval.gas('createRequest')
tx_approval = tx_approval_buildable.buildTransaction({
'from': self.address,
'gas': transfer_approval_gas,
'gasPrice': self.gas_price,
'chainId': chain_spec.chain_id(),
'nonce': self.next_nonce(),
})
return tx_approval
def unpack_transfer_approval_request(data):
"""Verifies that a transaction is an "TransferApproval.request" 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
"""
f = data[2:10]
if f != contract_function_signatures['request']:
raise ValueError('Invalid transfer request data ({})'.format(f))
d = data[10:]
return {
'to': web3.Web3.toChecksumAddress('0x' + d[64-40:64]),
'token': web3.Web3.toChecksumAddress('0x' + d[128-40:128]),
'amount': int(d[128:], 16)
}
@celery_app.task(bind=True)
def transfer_approval_request(self, tokens, holder_address, receiver_address, value, chain_str):
"""Creates a new transfer approval
:param tokens: Token to generate transfer request for
:type tokens: list with single token spec as dict
:param holder_address: Address to generate transfer on behalf of
:type holder_address: str, 0x-hex
:param receiver_address: Address to transfser tokens to
:type receiver_address: str, 0x-hex
:param value: Amount of tokens to transfer
:type value: number
:param chain_spec: Chain spec string representation
:type chain_spec: str
:raises cic_eth.error.TokenCountError: More than one token in tokens argument
:returns: Raw signed transaction
:rtype: list with transaction as only element
"""
if len(tokens) != 1:
raise TokenCountError
chain_spec = ChainSpec.from_chain_str(chain_str)
queue = self.request.delivery_info['routing_key']
t = tokens[0]
c = RpcClient(holder_address)
txf = TransferRequestTxFactory(holder_address, c)
tx_transfer = txf.request(t['address'], receiver_address, value, chain_spec)
(tx_hash_hex, tx_signed_raw_hex) = sign_and_register_tx(tx_transfer, chain_str, queue, 'cic_eth.eth.request.otx_cache_transfer_approval_request')
gas_budget = tx_transfer['gas'] * tx_transfer['gasPrice']
s = create_check_gas_and_send_task(
[tx_signed_raw_hex],
chain_str,
holder_address,
gas_budget,
[tx_hash_hex],
queue,
)
s.apply_async()
return [tx_signed_raw_hex]
@celery_app.task()
def otx_cache_transfer_approval_request(
tx_hash_hex,
tx_signed_raw_hex,
chain_str,
):
"""Generates and commits transaction cache metadata for an TransferApproval.request transaction
:param tx_hash_hex: Transaction hash
:type tx_hash_hex: str, 0x-hex
:param tx_signed_raw_hex: Raw signed transaction
:type tx_signed_raw_hex: str, 0x-hex
:param chain_str: Chain spec string representation
:type chain_str: str
:returns: Transaction hash and id of cache element in storage backend, respectively
:rtype: tuple
"""
chain_spec = ChainSpec.from_chain_str(chain_str)
tx_signed_raw_bytes = bytes.fromhex(tx_signed_raw_hex[2:])
tx = unpack_signed_raw_tx(tx_signed_raw_bytes, chain_spec.chain_id())
logg.debug('in otx acche transfer approval request')
(txc, cache_id) = cache_transfer_approval_request_data(tx_hash_hex, tx)
return txc
@celery_app.task()
def cache_transfer_approval_request_data(
tx_hash_hex,
tx,
):
"""Helper function for otx_cache_transfer_approval_request
:param tx_hash_hex: Transaction hash
:type tx_hash_hex: str, 0x-hex
:param tx: Signed raw transaction
:type tx: str, 0x-hex
:returns: Transaction hash and id of cache element in storage backend, respectively
:rtype: tuple
"""
tx_data = unpack_transfer_approval_request(tx['data'])
logg.debug('tx approval request data {}'.format(tx_data))
logg.debug('tx approval request {}'.format(tx))
session = SessionBase.create_session()
tx_cache = TxCache(
tx_hash_hex,
tx['from'],
tx_data['to'],
tx_data['token'],
tx_data['token'],
tx_data['amount'],
tx_data['amount'],
)
session.add(tx_cache)
session.commit()
cache_id = tx_cache.id
session.close()
return (tx_hash_hex, cache_id)

View File

@ -69,7 +69,6 @@ def check_gas(self, tx_hashes, chain_str, txs=[], address=None, gas_required=Non
for i in range(len(tx_hashes)): for i in range(len(tx_hashes)):
o = get_tx(tx_hashes[i]) o = get_tx(tx_hashes[i])
txs.append(o['signed_tx']) txs.append(o['signed_tx'])
logg.debug('ooooo {}'.format(o))
if address == None: if address == None:
address = o['address'] address = o['address']
@ -178,12 +177,7 @@ class ParityNodeHandler:
def handle(self, exception, tx_hash_hex, tx_hex): def handle(self, exception, tx_hash_hex, tx_hex):
meth = self.handle_default meth = self.handle_default
if isinstance(exception, (ValueError)): if isinstance(exception, (ValueError)):
# s_debug = celery.signature(
# 'cic_eth.admin.debug.out_tmp',
# [tx_hash_hex, '{}: {}'.format(tx_hash_hex, exception)],
# queue=queue,
# )
# s_debug.apply_async()
earg = exception.args[0] earg = exception.args[0]
if earg['code'] == -32010: if earg['code'] == -32010:
logg.debug('skipping lock for code {}'.format(earg['code'])) logg.debug('skipping lock for code {}'.format(earg['code']))
@ -191,14 +185,15 @@ class ParityNodeHandler:
elif earg['code'] == -32602: elif earg['code'] == -32602:
meth = self.handle_invalid_encoding meth = self.handle_invalid_encoding
else: else:
# TODO: move to status log db comment field
meth = self.handle_invalid meth = self.handle_invalid
elif isinstance(exception, (requests.exceptions.ConnectionError)): elif isinstance(exception, (requests.exceptions.ConnectionError)):
meth = self.handle_connection meth = self.handle_connection
(t, e_fn, message) = meth(tx_hash_hex, tx_hex) (t, e_fn, message) = meth(tx_hash_hex, tx_hex, str(exception))
return (t, e_fn, '{} {}'.format(message, exception)) return (t, e_fn, '{} {}'.format(message, exception))
def handle_connection(self, tx_hash_hex, tx_hex): def handle_connection(self, tx_hash_hex, tx_hex, debugstr=None):
s_set_sent = celery.signature( s_set_sent = celery.signature(
'cic_eth.queue.tx.set_sent_status', 'cic_eth.queue.tx.set_sent_status',
[ [
@ -211,7 +206,7 @@ class ParityNodeHandler:
return (t, TemporaryTxError, 'Sendfail {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id()))) return (t, TemporaryTxError, 'Sendfail {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id())))
def handle_invalid_encoding(self, tx_hash_hex, tx_hex): def handle_invalid_encoding(self, tx_hash_hex, tx_hex, debugstr=None):
tx_bytes = bytes.fromhex(tx_hex[2:]) tx_bytes = bytes.fromhex(tx_hex[2:])
tx = unpack_signed_raw_tx(tx_bytes, self.chain_spec.chain_id()) tx = unpack_signed_raw_tx(tx_bytes, self.chain_spec.chain_id())
s_lock = celery.signature( s_lock = celery.signature(
@ -258,7 +253,7 @@ class ParityNodeHandler:
return (t, PermanentTxError, 'Reject invalid encoding {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id()))) return (t, PermanentTxError, 'Reject invalid encoding {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id())))
def handle_invalid_parameters(self, tx_hash_hex, tx_hex): def handle_invalid_parameters(self, tx_hash_hex, tx_hex, debugstr=None):
s_sync = celery.signature( s_sync = celery.signature(
'cic_eth.eth.tx.sync_tx', 'cic_eth.eth.tx.sync_tx',
[ [
@ -271,7 +266,7 @@ class ParityNodeHandler:
return (t, PermanentTxError, 'Reject invalid parameters {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id()))) return (t, PermanentTxError, 'Reject invalid parameters {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id())))
def handle_invalid(self, tx_hash_hex, tx_hex): def handle_invalid(self, tx_hash_hex, tx_hex, debugstr=None):
tx_bytes = bytes.fromhex(tx_hex[2:]) tx_bytes = bytes.fromhex(tx_hex[2:])
tx = unpack_signed_raw_tx(tx_bytes, self.chain_spec.chain_id()) tx = unpack_signed_raw_tx(tx_bytes, self.chain_spec.chain_id())
s_lock = celery.signature( s_lock = celery.signature(
@ -289,6 +284,16 @@ class ParityNodeHandler:
[], [],
queue=self.queue, queue=self.queue,
) )
s_debug = celery.signature(
'cic_eth.admin.debug.alert',
[
tx_hash_hex,
tx_hash_hex,
debugstr,
],
queue=queue,
)
s_set_reject.link(s_debug)
s_lock.link(s_set_reject) s_lock.link(s_set_reject)
t = s_lock.apply_async() t = s_lock.apply_async()
return (t, PermanentTxError, 'Reject invalid {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id()))) return (t, PermanentTxError, 'Reject invalid {}'.format(tx_hex_string(tx_hex, self.chain_spec.chain_id())))

View File

@ -30,6 +30,7 @@ from cic_eth.task import CriticalSQLAlchemyTask
from cic_eth.eth.util import unpack_signed_raw_tx # TODO: should not be in same sub-path as package that imports queue.tx from cic_eth.eth.util import unpack_signed_raw_tx # TODO: should not be in same sub-path as package that imports queue.tx
from cic_eth.error import NotLocalTxError from cic_eth.error import NotLocalTxError
from cic_eth.error import LockedError from cic_eth.error import LockedError
from cic_eth.db.enum import status_str
celery_app = celery.current_app celery_app = celery.current_app
#logg = celery_app.log.get_default_logger() #logg = celery_app.log.get_default_logger()
@ -405,7 +406,7 @@ def get_tx_cache(tx_hash):
'tx_hash': otx.tx_hash, 'tx_hash': otx.tx_hash,
'signed_tx': otx.signed_tx, 'signed_tx': otx.signed_tx,
'nonce': otx.nonce, 'nonce': otx.nonce,
'status': StatusEnum(otx.status).name, 'status': status_str(otx.status),
'status_code': otx.status, 'status_code': otx.status,
'source_token': txc.source_token_address, 'source_token': txc.source_token_address,
'destination_token': txc.destination_token_address, 'destination_token': txc.destination_token_address,

View File

@ -2,3 +2,4 @@ from .callback import CallbackFilter
from .tx import TxFilter from .tx import TxFilter
from .gas import GasFilter from .gas import GasFilter
from .register import RegistrationFilter from .register import RegistrationFilter
from .transferauth import TransferAuthFilter

View File

@ -1,7 +1,7 @@
# standard imports # standard imports
import logging import logging
# third-party imports # external imports
from cic_registry.chain import ChainSpec from cic_registry.chain import ChainSpec
from hexathon import add_0x from hexathon import add_0x
@ -24,7 +24,7 @@ class GasFilter(SyncFilter):
self.chain_spec = chain_spec self.chain_spec = chain_spec
def filter(self, conn, block, tx, session): #rcpt, chain_str, session=None): def filter(self, conn, block, tx, session):
tx_hash_hex = add_0x(tx.hash) tx_hash_hex = add_0x(tx.hash)
if tx.value > 0: if tx.value > 0:
logg.debug('gas refill tx {}'.format(tx_hash_hex)) logg.debug('gas refill tx {}'.format(tx_hash_hex))

View File

@ -0,0 +1,89 @@
# standard imports
import logging
# external imports
import celery
from hexathon import (
strip_0x,
add_0x,
)
from chainlib.eth.address import to_checksum
from .base import SyncFilter
logg = logging.getLogger(__name__)
transfer_request_signature = 'ed71262a'
def unpack_create_request(data):
data = strip_0x(data)
cursor = 0
f = data[cursor:cursor+8]
cursor += 8
if f != transfer_request_signature:
raise ValueError('Invalid create request data ({})'.format(f))
o = {}
o['sender'] = data[cursor+24:cursor+64]
cursor += 64
o['recipient'] = data[cursor+24:cursor+64]
cursor += 64
o['token'] = data[cursor+24:cursor+64]
cursor += 64
o['value'] = int(data[cursor:], 16)
return o
class TransferAuthFilter(SyncFilter):
def __init__(self, registry, chain_spec, queue=None):
self.queue = queue
self.chain_spec = chain_spec
self.transfer_request_contract = registry.get_contract(self.chain_spec, 'TransferAuthorization')
def filter(self, conn, block, tx, session): #rcpt, chain_str, session=None):
if tx.payload == None:
logg.debug('no payload')
return False
payloadlength = len(tx.payload)
if payloadlength != 8+256:
logg.debug('{} below minimum length for a transfer auth call'.format(payloadlength))
logg.debug('payload {}'.format(tx.payload))
return False
recipient = tx.inputs[0]
if recipient != self.transfer_request_contract.address():
logg.debug('not our transfer auth contract address {}'.format(recipient))
return False
o = unpack_create_request(tx.payload)
sender = add_0x(to_checksum(o['sender']))
recipient = add_0x(to_checksum(recipient))
token = add_0x(to_checksum(o['token']))
s = celery.signature(
'cic_eth.eth.token.approve',
[
[
{
'address': token,
},
],
sender,
recipient,
o['value'],
str(self.chain_spec),
],
queue=self.queue,
)
t = s.apply_async()
return True
def __str__(self):
return 'cic-eth transfer auth filter'

View File

@ -55,62 +55,25 @@ SessionBase.connect(dsn)
celery_app = celery.Celery(backend=config.get('CELERY_RESULT_URL'), broker=config.get('CELERY_BROKER_URL')) celery_app = celery.Celery(backend=config.get('CELERY_RESULT_URL'), broker=config.get('CELERY_BROKER_URL'))
queue = args.q queue = args.q
re_transfer_approval_request = r'^/transferrequest/?' re_something = r'^/something/?'
chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC')) chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC'))
def process_transfer_approval_request(session, env): def process_something(session, env):
r = re.match(re_transfer_approval_request, env.get('PATH_INFO')) r = re.match(re_something, env.get('PATH_INFO'))
if not r: if not r:
return None return None
if env.get('CONTENT_TYPE') != 'application/json': #if env.get('CONTENT_TYPE') != 'application/json':
raise AttributeError('content type') # raise AttributeError('content type')
if env.get('REQUEST_METHOD') != 'POST': #if env.get('REQUEST_METHOD') != 'POST':
raise AttributeError('method') # raise AttributeError('method')
post_data = json.load(env.get('wsgi.input')) #post_data = json.load(env.get('wsgi.input'))
token_address = web3.Web3.toChecksumAddress(post_data['token_address'])
holder_address = web3.Web3.toChecksumAddress(post_data['holder_address']) #return ('text/plain', 'foo'.encode('utf-8'),)
beneficiary_address = web3.Web3.toChecksumAddress(post_data['beneficiary_address'])
value = int(post_data['value'])
logg.debug('transfer approval request token {} to {} from {} value {}'.format(
token_address,
beneficiary_address,
holder_address,
value,
)
)
s = celery.signature(
'cic_eth.eth.request.transfer_approval_request',
[
[
{
'address': token_address,
},
],
holder_address,
beneficiary_address,
value,
config.get('CIC_CHAIN_SPEC'),
],
queue=queue,
)
t = s.apply_async()
r = t.get()
tx_raw_bytes = bytes.fromhex(r[0][2:])
tx = unpack_signed_raw_tx(tx_raw_bytes, chain_spec.chain_id())
for r in t.collect():
logg.debug('result {}'.format(r))
if not t.successful():
raise RuntimeError(tx['hash'])
return ('text/plain', tx['hash'].encode('utf-8'),)
# uwsgi application # uwsgi application
@ -125,7 +88,7 @@ def application(env, start_response):
session = SessionBase.create_session() session = SessionBase.create_session()
for handler in [ for handler in [
process_transfer_approval_request, process_something,
]: ]:
try: try:
r = handler(session, env) r = handler(session, env)

View File

@ -26,7 +26,6 @@ from cic_eth.eth import bancor
from cic_eth.eth import token from cic_eth.eth import token
from cic_eth.eth import tx from cic_eth.eth import tx
from cic_eth.eth import account from cic_eth.eth import account
from cic_eth.eth import request
from cic_eth.admin import debug from cic_eth.admin import debug
from cic_eth.admin import ctrl from cic_eth.admin import ctrl
from cic_eth.eth.rpc import RpcClient from cic_eth.eth.rpc import RpcClient
@ -40,6 +39,7 @@ from cic_eth.callbacks import redis
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
from cic_eth.db.models.otx import Otx from cic_eth.db.models.otx import Otx
from cic_eth.db import dsn_from_config from cic_eth.db import dsn_from_config
from cic_eth.ext import tx
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()

View File

@ -58,6 +58,7 @@ from cic_eth.runnable.daemons.filters import (
GasFilter, GasFilter,
TxFilter, TxFilter,
RegistrationFilter, RegistrationFilter,
TransferAuthFilter,
) )
script_dir = os.path.realpath(os.path.dirname(__file__)) script_dir = os.path.realpath(os.path.dirname(__file__))
@ -146,6 +147,8 @@ def main():
gas_filter = GasFilter(chain_spec, config.get('_CELERY_QUEUE')) gas_filter = GasFilter(chain_spec, config.get('_CELERY_QUEUE'))
transfer_auth_filter = TransferAuthFilter(registry, chain_spec, config.get('_CELERY_QUEUE'))
i = 0 i = 0
for syncer in syncers: for syncer in syncers:
logg.debug('running syncer index {}'.format(i)) logg.debug('running syncer index {}'.format(i))
@ -153,6 +156,7 @@ def main():
syncer.add_filter(registration_filter) syncer.add_filter(registration_filter)
# TODO: the two following filter functions break the filter loop if return uuid. Pro: less code executed. Con: Possibly unintuitive flow break # TODO: the two following filter functions break the filter loop if return uuid. Pro: less code executed. Con: Possibly unintuitive flow break
syncer.add_filter(tx_filter) syncer.add_filter(tx_filter)
syncer.add_filter(transfer_auth_filter)
for cf in callback_filters: for cf in callback_filters:
syncer.add_filter(cf) syncer.add_filter(cf)

View File

@ -23,8 +23,11 @@ from hexathon import add_0x
# local imports # local imports
from cic_eth.api import AdminApi from cic_eth.api import AdminApi
from cic_eth.eth.rpc import RpcClient from cic_eth.eth.rpc import RpcClient
from cic_eth.db.enum import StatusEnum from cic_eth.db.enum import (
from cic_eth.db.enum import LockEnum StatusEnum,
status_str,
LockEnum,
)
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
@ -119,7 +122,7 @@ def render_tx(o, **kwargs):
for v in o.get('status_log', []): for v in o.get('status_log', []):
d = datetime.datetime.fromisoformat(v[0]) d = datetime.datetime.fromisoformat(v[0])
e = StatusEnum(v[1]).name e = status_str(v[1])
content += '{}: {}\n'.format(d, e) content += '{}: {}\n'.format(d, e)
return content return content

View File

@ -10,7 +10,7 @@ version = (
0, 0,
10, 10,
0, 0,
'alpha.36', 'alpha.37',
) )
version_object = semver.VersionInfo( version_object = semver.VersionInfo(

View File

@ -1,6 +1,7 @@
cic-base~=0.1.1a10
web3==5.12.2 web3==5.12.2
celery==4.4.7 celery==4.4.7
crypto-dev-signer~=0.4.13rc3 crypto-dev-signer~=0.4.13rc4
confini~=0.3.6rc3 confini~=0.3.6rc3
cic-registry~=0.5.3a22 cic-registry~=0.5.3a22
cic-bancor~=0.0.6 cic-bancor~=0.0.6
@ -9,7 +10,7 @@ alembic==1.4.2
websockets==8.1 websockets==8.1
requests~=2.24.0 requests~=2.24.0
eth_accounts_index~=0.0.10a10 eth_accounts_index~=0.0.10a10
erc20-approval-escrow~=0.3.0a5 erc20-transfer-authorization~=0.3.0a10
erc20-single-shot-faucet~=0.2.0a6 erc20-single-shot-faucet~=0.2.0a6
rlp==2.0.1 rlp==2.0.1
uWSGI==2.0.19.1 uWSGI==2.0.19.1
@ -21,4 +22,3 @@ eth-address-index~=0.1.0a8
chainlib~=0.0.1a19 chainlib~=0.0.1a19
hexathon~=0.0.1a3 hexathon~=0.0.1a3
chainsyncer~=0.0.1a19 chainsyncer~=0.0.1a19
cic-base==0.1.1a10

View File

@ -13,13 +13,13 @@ def celery_includes():
return [ return [
'cic_eth.eth.bancor', 'cic_eth.eth.bancor',
'cic_eth.eth.token', 'cic_eth.eth.token',
'cic_eth.eth.request',
'cic_eth.eth.tx', 'cic_eth.eth.tx',
'cic_eth.ext.tx', 'cic_eth.ext.tx',
'cic_eth.queue.tx', 'cic_eth.queue.tx',
'cic_eth.queue.balance', 'cic_eth.queue.balance',
'cic_eth.admin.ctrl', 'cic_eth.admin.ctrl',
'cic_eth.admin.nonce', 'cic_eth.admin.nonce',
'cic_eth.admin.debug',
'cic_eth.eth.account', 'cic_eth.eth.account',
'cic_eth.callbacks.noop', 'cic_eth.callbacks.noop',
'cic_eth.callbacks.http', 'cic_eth.callbacks.http',

View File

@ -0,0 +1,29 @@
# external imports
import celery
# local imports
from cic_eth.db.models.debug import Debug
def test_debug_alert(
init_database,
celery_session_worker,
):
s = celery.signature(
'cic_eth.admin.debug.alert',
[
'foo',
'bar',
'baz',
],
queue=None,
)
t = s.apply_async()
r = t.get()
assert r == 'foo'
q = init_database.query(Debug)
q = q.filter(Debug.tag=='bar')
o = q.first()
assert o.description == 'baz'

View File

@ -1,76 +0,0 @@
# standard imports
import logging
import time
# third-party imports
from erc20_approval_escrow import TransferApproval
import celery
import sha3
# local imports
from cic_eth.eth.token import TokenTxFactory
logg = logging.getLogger()
# BUG: transaction receipt only found sometimes
def test_transfer_approval(
default_chain_spec,
transfer_approval,
bancor_tokens,
w3_account_roles,
eth_empty_accounts,
cic_registry,
init_database,
celery_session_worker,
init_eth_tester,
init_w3,
):
s = celery.signature(
'cic_eth.eth.request.transfer_approval_request',
[
[
{
'address': bancor_tokens[0],
},
],
w3_account_roles['eth_account_sarafu_owner'],
eth_empty_accounts[0],
1024,
str(default_chain_spec),
],
)
s_send = celery.signature(
'cic_eth.eth.tx.send',
[
str(default_chain_spec),
],
)
s.link(s_send)
t = s.apply_async()
tx_signed_raws = t.get()
for r in t.collect():
logg.debug('result {}'.format(r))
assert t.successful()
init_eth_tester.mine_block()
h = sha3.keccak_256()
tx_signed_raw = tx_signed_raws[0]
tx_signed_raw_bytes = bytes.fromhex(tx_signed_raw[2:])
h.update(tx_signed_raw_bytes)
tx_hash = h.digest()
rcpt = init_w3.eth.getTransactionReceipt(tx_hash)
assert rcpt.status == 1
a = TransferApproval(init_w3, transfer_approval)
assert a.last_serial() == 1
logg.debug('requests {}'.format(a.requests(1)['serial']))

View File

@ -0,0 +1,16 @@
# local imports
from cic_eth.db.models.debug import Debug
def test_debug(
init_database,
):
o = Debug('foo', 'bar')
init_database.add(o)
init_database.commit()
q = init_database.query(Debug)
q = q.filter(Debug.tag=='foo')
o = q.first()
assert o.description == 'bar'

View File

@ -35,7 +35,7 @@ if [[ -n "${ETH_PROVIDER}" ]]; then
CIC_ACCOUNTS_INDEX_ADDRESS=`eth-accounts-index-deploy -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -y $keystore_file --writer $DEV_ETH_ACCOUNT_ACCOUNTS_INDEX_WRITER -vv -w` CIC_ACCOUNTS_INDEX_ADDRESS=`eth-accounts-index-deploy -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -y $keystore_file --writer $DEV_ETH_ACCOUNT_ACCOUNTS_INDEX_WRITER -vv -w`
CIC_REGISTRY_ADDRESS=`cic-registry-deploy -i $CIC_CHAIN_SPEC -y $keystore_file -k CICRegistry -k BancorRegistry -k AccountRegistry -k TokenRegistry -k AddressDeclarator -k Faucet -k TransferApproval -p $ETH_PROVIDER -vv -w` CIC_REGISTRY_ADDRESS=`cic-registry-deploy -i $CIC_CHAIN_SPEC -y $keystore_file -k CICRegistry -k BancorRegistry -k AccountRegistry -k TokenRegistry -k AddressDeclarator -k Faucet -k TransferAuthorization -p $ETH_PROVIDER -vv -w`
cic-registry-set -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -k CICRegistry -p $ETH_PROVIDER $CIC_REGISTRY_ADDRESS -vv cic-registry-set -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -k CICRegistry -p $ETH_PROVIDER $CIC_REGISTRY_ADDRESS -vv
#cic-registry-set -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -k BancorRegistry -p $ETH_PROVIDER $BANCOR_REGISTRY_ADDRESS -vv #cic-registry-set -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -k BancorRegistry -p $ETH_PROVIDER $BANCOR_REGISTRY_ADDRESS -vv
cic-registry-set -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -k AccountRegistry -p $ETH_PROVIDER $CIC_ACCOUNTS_INDEX_ADDRESS -vv cic-registry-set -y $keystore_file -r $CIC_REGISTRY_ADDRESS -i $CIC_CHAIN_SPEC -k AccountRegistry -p $ETH_PROVIDER $CIC_ACCOUNTS_INDEX_ADDRESS -vv

View File

@ -148,7 +148,11 @@ class Handler:
return return
u = Person.deserialize(o) u = Person.deserialize(o)
original_address = u.identities[old_chain_spec.engine()]['{}:{}'.format(old_chain_spec.common_name(), old_chain_spec.network_id())][0] original_address = u.identities[old_chain_spec.engine()]['{}:{}'.format(old_chain_spec.common_name(), old_chain_spec.network_id())][0]
balance = self.balances[original_address] try:
balance = self.balances[original_address]
except KeyError as e:
logg.error('balance get fail orig {} new {}'.format(original_address, recipient))
return
# TODO: store token object in handler ,get decimals from there # TODO: store token object in handler ,get decimals from there
multiplier = 10**6 multiplier = 10**6

View File

@ -1,3 +1,3 @@
cic-base[full_graph]==0.1.1a10 cic-base[full_graph]==0.1.1a12
cic-eth==0.10.0a36 cic-eth==0.10.0a37
cic-types==0.1.0a8 cic-types==0.1.0a8

View File

@ -31,7 +31,8 @@ set -e
set -a set -a
# We need to not install these here... # We need to not install these here...
pip install --extra-index-url $DEV_PIP_EXTRA_INDEX_URL cic-eth==0.10.0a36 chainlib==0.0.1a19 cic-contracts==0.0.2a2 pip install --extra-index-url $DEV_PIP_EXTRA_INDEX_URL cic-eth==0.10.0a37 chainlib==0.0.1a19 cic-contracts==0.0.2a2
pip install --extra-index-url $DEV_PIP_EXTRA_INDEX_URL --force-reinstall erc20-transfer-authorization==0.3.0a10
>&2 echo "create account for gas gifter" >&2 echo "create account for gas gifter"
old_gas_provider=$DEV_ETH_ACCOUNT_GAS_PROVIDER old_gas_provider=$DEV_ETH_ACCOUNT_GAS_PROVIDER
@ -89,13 +90,13 @@ export DEV_ETH_SARAFU_TOKEN_ADDRESS=$DEV_ETH_RESERVE_ADDRESS
>&2 echo "deploy transfer authorization contract" >&2 echo "deploy transfer authorization contract"
#CIC_TRANSFER_AUTHORIZATION_ADDRESS=`erc20-approval-escrow-deploy -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER --approver $DEV_ETH_ACCOUNT_TRANSFER_AUTHORIZATION_OWNER -w $debug` CIC_TRANSFER_AUTHORIZATION_ADDRESS=`erc20-transfer-auth-deploy -y $keystore_file -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER --approver $DEV_ETH_ACCOUNT_TRANSFER_AUTHORIZATION_OWNER -w $debug`
#echo CIC_APPROVAL_ESCROW_ADDRESS=$CIC_TRANSFER_AUTHORIZATION_ADDRESS >> $env_out_file echo CIC_TRANSFER_AUTHORIZATION_ADDRESS=$CIC_TRANSFER_AUTHORIZATION_ADDRESS >> $env_out_file
#export CIC_TRANSFER_AUTHORIZATION_ADDRESS=$CIC_TRANSFER_AUTHORIZATION_ADDRESS export CIC_TRANSFER_AUTHORIZATION_ADDRESS=$CIC_TRANSFER_AUTHORIZATION_ADDRESS
# Register transfer approval contract # Register transfer approval contract
#>&2 echo "add transfer approval request contract to registry" >&2 echo "add transfer authorization request contract to registry"
#>&2 cic-registry-set -y $keystore_file -r $CIC_REGISTRY_ADDRESS -k TransferApproval -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w $debug $CIC_TRANSFER_AUTHORIZATION_ADDRESS >&2 cic-registry-set -y $keystore_file -r $CIC_REGISTRY_ADDRESS -k TransferAuthorization -i $CIC_CHAIN_SPEC -p $ETH_PROVIDER -w $debug $CIC_TRANSFER_AUTHORIZATION_ADDRESS
# Deploy one-time token faucet for newly created token # Deploy one-time token faucet for newly created token

View File

@ -3,7 +3,7 @@ alembic==1.4.2
bcrypt==3.2.0 bcrypt==3.2.0
celery==4.4.7 celery==4.4.7
confini==0.3.6rc3 confini==0.3.6rc3
crypto-dev-signer==0.4.13rc3 crypto-dev-signer==0.4.13rc4
cryptography==3.2.1 cryptography==3.2.1
ecuth==0.4.5a1 ecuth==0.4.5a1
eth-accounts-index==0.0.10a10 eth-accounts-index==0.0.10a10

View File

@ -161,7 +161,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
/usr/local/bin/cic-cache-tracker -v /usr/local/bin/cic-cache-tracker -vv
volumes: volumes:
- contract-config:/tmp/cic/config/:ro - contract-config:/tmp/cic/config/:ro
@ -192,7 +192,7 @@ services:
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
"/usr/local/bin/uwsgi" \ "/usr/local/bin/uwsgi" \
--wsgi-file /usr/src/cic-cache/cic_cache/runnable/server.py \ --wsgi-file /usr/src/cic-cache/cic_cache/runnable/server.py \
--http :80 \ --http :8000 \
--pyargv -vv --pyargv -vv
cic-eth-tasker: cic-eth-tasker:
@ -237,7 +237,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 -q cic-eth -v
# 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: