Initial commit
This commit is contained in:
1
cic_sync_filter/__init__.py
Normal file
1
cic_sync_filter/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
17
cic_sync_filter/base.py
Normal file
17
cic_sync_filter/base.py
Normal file
@@ -0,0 +1,17 @@
|
||||
class SyncFilter:
|
||||
|
||||
def __init__(self):
|
||||
self.exec_count = 0
|
||||
self.match_count = 0
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session):
|
||||
self.exec_count += 1
|
||||
|
||||
|
||||
def register_match(self):
|
||||
self.match_count += 1
|
||||
|
||||
|
||||
def to_logline(self, block, tx, v):
|
||||
return '{} exec {} match {} block {} tx {}: {}'.format(self, self.exec_count, self.match_count, block.number, tx.index, v)
|
||||
134
cic_sync_filter/callback.py
Normal file
134
cic_sync_filter/callback.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from cic_eth_registry.error import (
|
||||
UnknownContractError,
|
||||
NotAContractError,
|
||||
)
|
||||
from chainlib.status import Status as TxStatus
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from chainlib.eth.error import RequestMismatchException
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
from eth_erc20 import ERC20
|
||||
from erc20_faucet import Faucet
|
||||
from chainsyncer.filter import SyncFilter
|
||||
|
||||
# local imports
|
||||
#from cic_eth.eth.meta import ExtendedTx
|
||||
#from cic_eth.encode import tx_normalize
|
||||
from cic_sync_filter.parse import (
|
||||
parse_transfer,
|
||||
parse_transferfrom,
|
||||
parse_giftto,
|
||||
)
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CallbackFilter(SyncFilter):
|
||||
|
||||
trusted_addresses = []
|
||||
|
||||
def __init__(self, chain_spec, method, queue, caller_address=ZERO_ADDRESS):
|
||||
super(CallbackFilter, self).__init__()
|
||||
self.queue = queue
|
||||
self.method = method
|
||||
self.chain_spec = chain_spec
|
||||
self.caller_address = caller_address
|
||||
|
||||
|
||||
def call_back(self, transfer_type, result):
|
||||
result['chain_spec'] = result['chain_spec'].asdict()
|
||||
s = celery.signature(
|
||||
self.method,
|
||||
[
|
||||
result,
|
||||
transfer_type,
|
||||
int(result['status_code'] != 0),
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
t = s.apply_async()
|
||||
return t
|
||||
|
||||
|
||||
def parse_data(self, tx, conn):
|
||||
transfer_type = None
|
||||
transfer_data = None
|
||||
# TODO: what's with the mix of attributes and dict keys
|
||||
logg.debug('have payload {}'.format(tx.payload))
|
||||
|
||||
logg.debug('tx status {}'.format(tx.status))
|
||||
|
||||
for parser in [
|
||||
parse_transfer,
|
||||
parse_transferfrom,
|
||||
parse_giftto,
|
||||
]:
|
||||
try:
|
||||
if tx:
|
||||
(transfer_type, transfer_data) = parser(tx, conn, self.chain_spec, self.caller_address)
|
||||
if transfer_type == None:
|
||||
continue
|
||||
break
|
||||
except RequestMismatchException:
|
||||
continue
|
||||
|
||||
|
||||
logg.debug('resolved method {}'.format(transfer_type))
|
||||
|
||||
if transfer_data != None:
|
||||
transfer_data['status'] = tx.status
|
||||
|
||||
return (transfer_type, transfer_data)
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
super(CallbackFilter, self).filter(conn, block, tx, db_session)
|
||||
transfer_data = None
|
||||
transfer_type = None
|
||||
try:
|
||||
(transfer_type, transfer_data) = self.parse_data(tx, conn)
|
||||
except TypeError:
|
||||
logg.debug('invalid method data length for tx {}'.format(tx.hash))
|
||||
return
|
||||
|
||||
if len(tx.payload) < 8:
|
||||
logg.debug('callbacks filter data length not sufficient for method signature in tx {}, skipping'.format(tx.hash))
|
||||
return
|
||||
|
||||
logg.debug('checking callbacks filter input {}'.format(tx.payload[:8]))
|
||||
|
||||
t = None
|
||||
if transfer_data != None:
|
||||
token_symbol = None
|
||||
result = None
|
||||
try:
|
||||
tokentx = ExtendedTx(conn, tx.hash, self.chain_spec)
|
||||
tokentx.set_actors(transfer_data['from'], transfer_data['to'], self.trusted_addresses, caller_address=self.caller_address)
|
||||
tokentx.set_tokens(transfer_data['token_address'], transfer_data['value'])
|
||||
if transfer_data['status'] == 0:
|
||||
tokentx.set_status(1)
|
||||
else:
|
||||
tokentx.set_status(0)
|
||||
result = tokentx.asdict()
|
||||
t = self.call_back(transfer_type, result)
|
||||
self.register_match()
|
||||
logline = 'callback success task id {} tx {} queue {}'.format(t, tx.hash, t.queue)
|
||||
logline = self.to_logline(block, tx, logline)
|
||||
logg.info(logline)
|
||||
except UnknownContractError:
|
||||
logg.debug('callback filter {}:{} skipping "transfer" method on unknown contract {} tx {}'.format(self.queue, self.method, transfer_data['to'], tx.hash))
|
||||
except NotAContractError:
|
||||
logg.debug('callback filter {}:{} skipping "transfer" on non-contract address {} tx {}'.format(self.queue, self.method, transfer_data['to'], tx.hash))
|
||||
|
||||
return t
|
||||
|
||||
def __str__(self):
|
||||
return 'callbackfilter'
|
||||
61
cic_sync_filter/convert.py
Normal file
61
cic_sync_filter/convert.py
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
#__convert_log_hash = '0x7154b38b5dd31bb3122436a96d4e09aba5b323ae1fd580025fab55074334c095' # keccak256(Conversion(address,address,address,uint256,uint256,address)
|
||||
|
||||
#def parse_convert_log(w3, entry):
|
||||
# data = entry.data[2:]
|
||||
# from_amount = int(data[:64], 16)
|
||||
# to_amount = int(data[64:128], 16)
|
||||
# holder_address_hex_raw = '0x' + data[-40:]
|
||||
# holder_address_hex = w3.toChecksumAddress(holder_address_hex_raw)
|
||||
# o = {
|
||||
# 'from_amount': from_amount,
|
||||
# 'to_amount': to_amount,
|
||||
# 'holder_address': holder_address_hex
|
||||
# }
|
||||
# logg.debug('parsed convert log {}'.format(o))
|
||||
# return o
|
||||
|
||||
|
||||
#def convert_filter(w3, tx, rcpt, chain_spec):
|
||||
# destination_token_address = None
|
||||
# recipient_address = None
|
||||
# amount = 0
|
||||
# for l in rcpt['logs']:
|
||||
# event_topic_hex = l['topics'][0].hex()
|
||||
# if event_topic_hex == __convert_log_hash:
|
||||
# tx_hash_hex = tx['hash'].hex()
|
||||
# try:
|
||||
# convert_transfer = TxConvertTransfer.get(tx_hash_hex)
|
||||
# except UnknownConvertError:
|
||||
# logg.warning('skipping unknown convert tx {}'.format(tx_hash_hex))
|
||||
# continue
|
||||
# if convert_transfer.transfer_tx_hash != None:
|
||||
# logg.warning('convert tx {} cache record already has transfer hash {}, skipping'.format(tx_hash_hex, convert_transfer.transfer_hash))
|
||||
# continue
|
||||
# recipient_address = convert_transfer.recipient_address
|
||||
# logg.debug('found convert event {} recipient'.format(tx_hash_hex, recipient_address))
|
||||
# r = parse_convert_log(l)
|
||||
# destination_token_address = l['topics'][3][-20:]
|
||||
#
|
||||
# if destination_token_address == zero_address or destination_token_address == None:
|
||||
# return None
|
||||
#
|
||||
# destination_token_address_hex = destination_token_address.hex()
|
||||
# s = celery.signature(
|
||||
# 'cic_eth.eth.bancor.transfer_converted',
|
||||
# [
|
||||
# [{
|
||||
# 'address': w3.toChecksumAddress(destination_token_address_hex),
|
||||
# }],
|
||||
# r['holder_address'],
|
||||
# recipient_address,
|
||||
# r['to_amount'],
|
||||
# tx_hash_hex,
|
||||
# str(chain_spec),
|
||||
# ],
|
||||
# queue=queue,
|
||||
# )
|
||||
# logg.info('sending tx signature {}'.format(s))
|
||||
# t = s.apply_async()
|
||||
# logg.debug('submitted transfer after convert task uuid {} {}'.format(t, t.successful()))
|
||||
# return t
|
||||
80
cic_sync_filter/gas.py
Normal file
80
cic_sync_filter/gas.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
from hexathon import (
|
||||
add_0x,
|
||||
strip_0x,
|
||||
)
|
||||
from chainlib.eth.tx import unpack
|
||||
from chainqueue.db.enum import StatusBits
|
||||
from chainqueue.db.models.tx import TxCache
|
||||
from chainqueue.db.models.otx import Otx
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
|
||||
# local imports
|
||||
from cic_eth.db.models.base import SessionBase
|
||||
from cic_eth.eth.gas import create_check_gas_task
|
||||
from cic_eth.queue.query import get_paused_tx
|
||||
from cic_eth.encode import tx_normalize
|
||||
from .base import SyncFilter
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class GasFilter(SyncFilter):
|
||||
|
||||
def __init__(self, chain_spec, queue=None):
|
||||
super(GasFilter, self).__init__()
|
||||
self.queue = queue
|
||||
self.chain_spec = chain_spec
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session):
|
||||
super(GasFilter, self).filter(conn, block, tx, db_session)
|
||||
if tx.value > 0 or len(tx.payload) == 0:
|
||||
tx_hash_hex = add_0x(tx.hash)
|
||||
|
||||
sender_target = tx_normalize.wallet_address(tx.inputs[0])
|
||||
session = SessionBase.bind_session(db_session)
|
||||
q = session.query(TxCache.recipient)
|
||||
q = q.filter(TxCache.sender==sender_target)
|
||||
r = q.all()
|
||||
|
||||
logline = None
|
||||
if len(r) == 0:
|
||||
logline = 'unsolicited gas refill tx {}; cannot find {} among senders'.format(tx_hash_hex, tx.outputs[0])
|
||||
logline = self.to_logline(block, tx, logline)
|
||||
logg.info(logline)
|
||||
SessionBase.release_session(session)
|
||||
return None
|
||||
|
||||
self.register_match()
|
||||
|
||||
txs = get_paused_tx(self.chain_spec, status=StatusBits.GAS_ISSUES, sender=sender_target, session=session, decoder=unpack)
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
t = None
|
||||
address = to_checksum_address(sender_target)
|
||||
if len(txs) > 0:
|
||||
s = create_check_gas_task(
|
||||
list(txs.values()),
|
||||
self.chain_spec,
|
||||
address,
|
||||
0,
|
||||
tx_hashes_hex=list(txs.keys()),
|
||||
queue=self.queue,
|
||||
)
|
||||
t = s.apply_async()
|
||||
logline = 'resuming {} gas-in-waiting txs for {}'.format(len(txs), sender_target)
|
||||
else:
|
||||
logline = 'gas refill tx {}'.format(tx)
|
||||
|
||||
logline = self.to_logline(block, tx, logline)
|
||||
logg.info(logline)
|
||||
return t
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'gasfilter'
|
||||
9
cic_sync_filter/log.py
Normal file
9
cic_sync_filter/log.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from .base import SyncFilter
|
||||
|
||||
|
||||
class LogFilter(SyncFilter):
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
logg.debug('block {} tx {}'.format(block, tx))
|
||||
|
||||
|
||||
77
cic_sync_filter/parse.py
Normal file
77
cic_sync_filter/parse.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
|
||||
def parse_transfer(tx, conn, chain_spec, caller_address=ZERO_ADDRESS):
|
||||
if not tx.payload:
|
||||
return (None, None)
|
||||
r = ERC20.parse_transfer_request(tx.payload)
|
||||
transfer_data = {}
|
||||
transfer_data['to'] = tx_normalize.wallet_address(r[0])
|
||||
transfer_data['value'] = r[1]
|
||||
transfer_data['from'] = tx_normalize.wallet_address(tx.outputs[0])
|
||||
transfer_data['token_address'] = tx.inputs[0]
|
||||
return ('transfer', transfer_data)
|
||||
|
||||
|
||||
def parse_transferfrom(tx, conn, chain_spec, caller_address=ZERO_ADDRESS):
|
||||
if not tx.payload:
|
||||
return (None, None)
|
||||
r = ERC20.parse_transfer_from_request(tx.payload)
|
||||
transfer_data = {}
|
||||
transfer_data['from'] = tx_normalize.wallet_address(r[0])
|
||||
transfer_data['to'] = tx_normalize.wallet_address(r[1])
|
||||
transfer_data['value'] = r[2]
|
||||
transfer_data['token_address'] = tx.inputs[0]
|
||||
return ('transferfrom', transfer_data)
|
||||
|
||||
|
||||
def parse_gas(tx, conn, chain_spec, caller_address=ZERO_ADDRESS):
|
||||
r = (None, None,)
|
||||
if tx.value > 0 or len(tx.payload) == 0:
|
||||
transfer_data = {}
|
||||
transfer_data['to'] = tx_normalize.wallet_address(tx.inputs[0])
|
||||
transfer_data['from'] = tx_normalize.wallet_address(tx.outputs[0])
|
||||
transfer_data['value'] = tx.value
|
||||
transfer_data['token_address'] = None
|
||||
r = ('gas', transfer_data,)
|
||||
else:
|
||||
logg.info('value {} payload {}'.format(tx.value, tx.payload))
|
||||
return r
|
||||
|
||||
|
||||
def parse_giftto(tx, conn, chain_spec, caller_address=ZERO_ADDRESS):
|
||||
if not tx.payload:
|
||||
return (None, None)
|
||||
r = Faucet.parse_give_to_request(tx.payload)
|
||||
transfer_data = {}
|
||||
transfer_data['to'] = tx_normalize.wallet_address(r[0])
|
||||
transfer_data['value'] = tx.value
|
||||
transfer_data['from'] = tx_normalize.wallet_address(tx.outputs[0])
|
||||
#transfer_data['token_address'] = tx.inputs[0]
|
||||
faucet_contract = tx.inputs[0]
|
||||
|
||||
c = Faucet(chain_spec)
|
||||
|
||||
o = c.token(faucet_contract, sender_address=caller_address)
|
||||
r = conn.do(o)
|
||||
transfer_data['token_address'] = add_0x(c.parse_token(r))
|
||||
|
||||
o = c.token_amount(faucet_contract, sender_address=caller_address)
|
||||
r = conn.do(o)
|
||||
transfer_data['value'] = c.parse_token_amount(r)
|
||||
|
||||
return ('tokengift', transfer_data)
|
||||
|
||||
|
||||
|
||||
def parse_register(tx, conn, chain_spec, caller_address=ZERO_ADDRESS):
|
||||
if not tx.payload:
|
||||
return (None, None)
|
||||
r = AccountRegistry.parse_add_request(tx.payload)
|
||||
transfer_data = {
|
||||
'value': None,
|
||||
'token_address': None,
|
||||
}
|
||||
transfer_data['to'] = tx_normalize.wallet_address(r)
|
||||
transfer_data['from'] = tx_normalize.wallet_address(tx.outputs[0])
|
||||
return ('account_register', transfer_data,)
|
||||
|
||||
70
cic_sync_filter/register.py
Normal file
70
cic_sync_filter/register.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# third-party imports
|
||||
import celery
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from hexathon import (
|
||||
add_0x,
|
||||
strip_0x,
|
||||
)
|
||||
|
||||
# local imports
|
||||
from .base import SyncFilter
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
account_registry_add_log_hash = '0x9cc987676e7d63379f176ea50df0ae8d2d9d1141d1231d4ce15b5965f73c9430'
|
||||
|
||||
|
||||
class RegistrationFilter(SyncFilter):
|
||||
|
||||
def __init__(self, chain_spec, contract_address, queue=None):
|
||||
super(RegistrationFilter, self).__init__()
|
||||
self.chain_spec = chain_spec
|
||||
self.queue = queue
|
||||
self.contract_address = contract_address
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
super(RegistrationFilter, self).filter(conn, block, tx, db_session)
|
||||
if self.contract_address != tx.inputs[0]:
|
||||
logg.debug('not an account registry tx; {} != {}'.format(self.contract_address, tx.inputs[0]))
|
||||
return None
|
||||
|
||||
for l in tx.logs:
|
||||
event_topic_hex = l['topics'][0]
|
||||
if event_topic_hex == account_registry_add_log_hash:
|
||||
self.register_match()
|
||||
# TODO: use abi conversion method instead
|
||||
|
||||
address_hex = strip_0x(l['topics'][1])[64-40:]
|
||||
address = to_checksum_address(add_0x(address_hex))
|
||||
s_nonce = celery.signature(
|
||||
'cic_eth.eth.nonce.reserve_nonce',
|
||||
[
|
||||
address,
|
||||
self.chain_spec.asdict(),
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_gift = celery.signature(
|
||||
'cic_eth.eth.account.gift',
|
||||
[
|
||||
self.chain_spec.asdict(),
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_nonce.link(s_gift)
|
||||
t = s_nonce.apply_async()
|
||||
|
||||
logline = 'request token gift to {}'.format(address)
|
||||
logline = self.to_logline(block, tx, logline)
|
||||
logg.info(logline)
|
||||
|
||||
return t
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'registrationfilter'
|
||||
|
||||
68
cic_sync_filter/straggler.py
Normal file
68
cic_sync_filter/straggler.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from chainqueue.sql.state import (
|
||||
obsolete_by_cache,
|
||||
set_fubar,
|
||||
)
|
||||
from chainqueue.error import TxStateChangeError
|
||||
from hexathon import to_int as hex_to_int
|
||||
from chainlib.eth.gas import balance
|
||||
from chainqueue.sql.query import get_tx_cache
|
||||
from chainqueue.enum import StatusBits
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class StragglerFilter:
|
||||
|
||||
def __init__(self, chain_spec, gas_balance_threshold, queue='cic-eth'):
|
||||
self.chain_spec = chain_spec
|
||||
self.queue = queue
|
||||
self.gas_balance_threshold = gas_balance_threshold
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
txc = get_tx_cache(self.chain_spec, tx.hash, session=db_session)
|
||||
if txc['status_code'] & StatusBits.GAS_ISSUES > 0:
|
||||
o = balance(tx.outputs[0])
|
||||
r = conn.do(o)
|
||||
gas_balance = hex_to_int(r)
|
||||
|
||||
t = None
|
||||
if gas_balance < self.gas_balance_threshold:
|
||||
logg.debug('WAITFORGAS tx ignored since gas balance {} is below threshold {}'.format(gas_balance, self.gas_balance_threshold))
|
||||
s_touch = celery.signature(
|
||||
'cic_eth.queue.state.set_checked',
|
||||
[
|
||||
self.chain_spec.asdict(),
|
||||
tx.hash,
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
t = s_touch.apply_async()
|
||||
return t
|
||||
|
||||
|
||||
try:
|
||||
obsolete_by_cache(self.chain_spec, tx.hash, False, session=db_session)
|
||||
except TxStateChangeError:
|
||||
set_fubar(self.chain_spec, tx.hash, session=db_session)
|
||||
return False
|
||||
|
||||
s_send = celery.signature(
|
||||
'cic_eth.eth.gas.resend_with_higher_gas',
|
||||
[
|
||||
tx.hash,
|
||||
self.chain_spec.asdict(),
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
t = s_send.apply_async()
|
||||
return t
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'stragglerfilter'
|
||||
83
cic_sync_filter/token.py
Normal file
83
cic_sync_filter/token.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
from eth_erc20 import ERC20
|
||||
from chainlib.eth.contract import (
|
||||
ABIContractEncoder,
|
||||
ABIContractType,
|
||||
)
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from chainlib.eth.address import is_same_address
|
||||
from chainlib.eth.error import RequestMismatchException
|
||||
from cic_eth_registry import CICRegistry
|
||||
from cic_eth_registry.erc20 import ERC20Token
|
||||
from cic_eth_registry.error import UnknownContractError
|
||||
from eth_token_index import TokenUniqueSymbolIndex
|
||||
import celery
|
||||
|
||||
# local imports
|
||||
from .base import SyncFilter
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TokenFilter(SyncFilter):
|
||||
|
||||
def __init__(self, chain_spec, queue, call_address=ZERO_ADDRESS):
|
||||
super(TokenFilter, self).__init__()
|
||||
self.queue = queue
|
||||
self.chain_spec = chain_spec
|
||||
self.caller_address = call_address
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
super(TokenFilter, self).filter(conn, block, tx, db_session)
|
||||
if not tx.payload:
|
||||
return None
|
||||
|
||||
try:
|
||||
r = ERC20.parse_transfer_request(tx.payload)
|
||||
except RequestMismatchException:
|
||||
return None
|
||||
|
||||
token_address = tx.inputs[0]
|
||||
token = ERC20Token(self.chain_spec, conn, token_address)
|
||||
|
||||
registry = CICRegistry(self.chain_spec, conn)
|
||||
r = None
|
||||
try:
|
||||
r = registry.by_name(token.symbol, sender_address=self.caller_address)
|
||||
except UnknownContractError:
|
||||
logg.debug('token {} not in registry, skipping'.format(token.symbol))
|
||||
return None
|
||||
|
||||
if is_same_address(r, ZERO_ADDRESS):
|
||||
return None
|
||||
|
||||
self.register_match()
|
||||
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('transfer')
|
||||
method = enc.get()
|
||||
|
||||
s = celery.signature(
|
||||
'cic_eth.eth.gas.apply_gas_value_cache',
|
||||
[
|
||||
token_address,
|
||||
method,
|
||||
tx.gas_used,
|
||||
tx.hash,
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
t = s.apply_async()
|
||||
|
||||
logline = 'erc20 transfer {} {}'.format(token.symbol, tx.hash)
|
||||
logline = self.to_logline(block, tx, logline)
|
||||
logg.info(logline)
|
||||
return t
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'erc20 tx filter'
|
||||
90
cic_sync_filter/transferauth.py
Normal file
90
cic_sync_filter/transferauth.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from chainlib.eth.contract import (
|
||||
ABIContractType,
|
||||
abi_decode_single,
|
||||
)
|
||||
from cic_eth_registry import CICRegistry
|
||||
from erc20_transfer_authorization import TransferAuthorization
|
||||
|
||||
# local imports
|
||||
from cic_eth.encode import tx_normalize
|
||||
from .base import SyncFilter
|
||||
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TransferAuthFilter(SyncFilter):
|
||||
|
||||
def __init__(self, registry, chain_spec, conn, queue=None, call_address=ZERO_ADDRESS):
|
||||
self.queue = queue
|
||||
self.chain_spec = chain_spec
|
||||
registry = CICRegistry(chain_spec, conn)
|
||||
self.transfer_request_contract = registry.by_name('TransferAuthorization', sender_address=call_address)
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_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():
|
||||
if recipient != self.transfer_request_contract:
|
||||
logg.debug('not our transfer auth contract address {}'.format(recipient))
|
||||
return False
|
||||
|
||||
r = TransferAuthorization.parse_create_request_request(tx.payload)
|
||||
|
||||
sender = tx_normalize.wallet_address(r[0])
|
||||
recipient = tx_normalize.wallet_address(r[1])
|
||||
token = tx_normalize.executable_address(r[2])
|
||||
value = r[3]
|
||||
|
||||
token_data = {
|
||||
'address': token,
|
||||
}
|
||||
|
||||
s_nonce = celery.signature(
|
||||
'cic_eth.eth.nonce.reserve_nonce',
|
||||
[
|
||||
[token_data],
|
||||
self.chain_spec.asdict(),
|
||||
sender,
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_approve = celery.signature(
|
||||
'cic_eth.eth.erc20.approve',
|
||||
[
|
||||
sender,
|
||||
recipient,
|
||||
value,
|
||||
self.chain_spec.asdict(),
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_nonce.link(s_approve)
|
||||
t = s_nonce.apply_async()
|
||||
return t
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'cic-eth transfer auth filter'
|
||||
70
cic_sync_filter/tx.py
Normal file
70
cic_sync_filter/tx.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
import celery
|
||||
from hexathon import (
|
||||
add_0x,
|
||||
)
|
||||
from chainsyncer.db.models.base import SessionBase
|
||||
from chainqueue.db.models.otx import Otx
|
||||
from chainlib.status import Status
|
||||
|
||||
# local imports
|
||||
from .base import SyncFilter
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TxFilter(SyncFilter):
|
||||
|
||||
def __init__(self, chain_spec, queue):
|
||||
super(TxFilter, self).__init__()
|
||||
self.queue = queue
|
||||
self.chain_spec = chain_spec
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
super(TxFilter, self).filter(conn, block, tx, db_session)
|
||||
db_session = SessionBase.bind_session(db_session)
|
||||
tx_hash_hex = tx.hash
|
||||
otx = Otx.load(add_0x(tx_hash_hex), session=db_session)
|
||||
if otx == None:
|
||||
logg.debug('tx {} not found locally, skipping'.format(tx_hash_hex))
|
||||
return None
|
||||
|
||||
self.register_match()
|
||||
|
||||
db_session.flush()
|
||||
SessionBase.release_session(db_session)
|
||||
s_final_state = celery.signature(
|
||||
'cic_eth.queue.state.set_final',
|
||||
[
|
||||
self.chain_spec.asdict(),
|
||||
add_0x(tx_hash_hex),
|
||||
tx.block.number,
|
||||
tx.index,
|
||||
tx.status == Status.ERROR,
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
s_obsolete_state = celery.signature(
|
||||
'cic_eth.queue.state.obsolete',
|
||||
[
|
||||
self.chain_spec.asdict(),
|
||||
add_0x(tx_hash_hex),
|
||||
True,
|
||||
],
|
||||
queue=self.queue,
|
||||
)
|
||||
t = celery.group(s_obsolete_state, s_final_state)()
|
||||
|
||||
logline = 'otx filter match on {}'.format(otx.tx_hash)
|
||||
logline = self.to_logline(block, tx, logline)
|
||||
logg.info(logline)
|
||||
|
||||
return t
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return 'otx filter'
|
||||
Reference in New Issue
Block a user