Rehabilitate transfer, approve

Signed-off-by: nolash <dev@holbrook.no>
This commit is contained in:
nolash 2021-03-20 13:58:45 +01:00
parent c245a29a6b
commit 3f61a2007e
10 changed files with 372 additions and 347 deletions

View File

@ -161,7 +161,7 @@ class NonceReservation(SessionBase):
o = q.first()
if o == None:
raise IntegrityError('nonce {} for key {} address {}: {}'.format(nonce, key, address))
raise IntegrityError('nonce {} for key {} address {}'.format(nonce, key, address))
SessionBase.release_session(session)
session.delete(o)

View File

@ -46,80 +46,8 @@ from cic_eth.queue.tx import (
register_tx,
)
#logg = logging.getLogger(__name__)
logg = logging.getLogger()
celery_app = celery.current_app
#celery_app.log.setup_task_loggers(loglevel=logging.DEBUG)
#celery_app.log.redirect_stdouts_to_logger(logg, loglevel=logging.DEBUG)
#class AccountTxFactory(TxFactory):
# """Factory for creating account index contract transactions
# """
# def add(
# self,
# address,
# chain_spec,
# uuid,
# session=None,
# ):
# """Register an Ethereum account address with the on-chain account registry
#
# :param address: Ethereum account address to add
# :type address: str, 0x-hex
# :param chain_spec: Chain to build transaction for
# :type chain_spec: cic_registry.chain.ChainSpec
# :returns: Unsigned "AccountRegistry.add" transaction in standard Ethereum format
# :rtype: dict
# """
#
# c = self.registry.get_contract(chain_spec, 'AccountRegistry')
# f = c.function('add')
# tx_add_buildable = f(
# address,
# )
# gas = c.gas('add')
# tx_add = tx_add_buildable.buildTransaction({
# 'from': self.address,
# 'gas': gas,
# 'gasPrice': self.gas_price,
# 'chainId': chain_spec.chain_id(),
# 'nonce': self.next_nonce(uuid, session=session),
# 'value': 0,
# })
# return tx_add
#
#
# def gift(
# self,
# address,
# chain_spec,
# uuid,
# session=None,
# ):
# """Trigger the on-chain faucet to disburse tokens to the provided Ethereum account
#
# :param address: Ethereum account address to gift to
# :type address: str, 0x-hex
# :param chain_spec: Chain to build transaction for
# :type chain_spec: cic_registry.chain.ChainSpec
# :returns: Unsigned "Faucet.giveTo" transaction in standard Ethereum format
# :rtype: dict
# """
#
# c = self.registry.get_contract(chain_spec, 'Faucet')
# f = c.function('giveTo')
# tx_add_buildable = f(address)
# gas = c.gas('add')
# tx_add = tx_add_buildable.buildTransaction({
# 'from': self.address,
# 'gas': gas,
# 'gasPrice': self.gas_price,
# 'chainId': chain_spec.chain_id(),
# 'nonce': self.next_nonce(uuid, session=session),
# 'value': 0,
# })
# return tx_add
def unpack_register(data):
@ -183,16 +111,11 @@ def create(self, password, chain_str):
o = new_account()
a = conn.do(o)
#try:
# a = c.w3.eth.personal.new_account(password)
#except FileNotFoundError:
# pass
if a == None:
raise SignerError('create account')
logg.debug('created account {}'.format(a))
# Initialize nonce provider record for account
#session = SessionBase.create_session()
session = self.create_session()
Nonce.init(a, session=session)
session.commit()
@ -217,7 +140,6 @@ def register(self, account_address, chain_spec_dict, writer_address=None):
chain_spec = ChainSpec.from_dict(chain_spec_dict)
session = self.create_session()
#session = SessionBase.create_session()
if writer_address == None:
writer_address = AccountRole.get_address('ACCOUNT_REGISTRY_WRITER', session=session)
@ -243,7 +165,6 @@ def register(self, account_address, chain_spec_dict, writer_address=None):
(tx_hash_hex, tx_signed_raw_hex) = account_registry.add(account_registry_address, writer_address, account_address, tx_format=TxFormat.RLP_SIGNED)
# TODO: if cache task fails, task chain will not return
cache_task = 'cic_eth.eth.account.cache_account_data'
cache_task = None
# add transaction to queue
register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=cache_task, session=session)

View File

@ -10,6 +10,10 @@ from chainlib.chain import ChainSpec
from chainlib.status import Status as TxStatus
from chainlib.connection import RPCConnection
from chainlib.eth.erc20 import ERC20
from chainlib.eth.tx import (
TxFormat,
unpack,
)
from cic_eth_registry.erc20 import ERC20Token
from hexathon import strip_0x
@ -20,15 +24,18 @@ from cic_eth.db.models.base import SessionBase
from cic_eth.eth import RpcClient
from cic_eth.error import TokenCountError, PermanentTxError, OutOfGasError, NotLocalTxError
from cic_eth.queue.tx import register_tx
from cic_eth.eth.gas import create_check_gas_task
from cic_eth.eth.gas import (
create_check_gas_task,
MaxGasOracle,
)
#from cic_eth.eth.factory import TxFactory
from cic_eth.eth.util import unpack_signed_raw_tx
from cic_eth.ext.address import translate_address
from cic_eth.task import (
CriticalSQLAlchemyTask,
CriticalWeb3Task,
CriticalSQLAlchemyAndSignerTask,
)
from cic_eth.eth.nonce import CustodialTaskNonceOracle
celery_app = celery.current_app
logg = logging.getLogger()
@ -87,7 +94,7 @@ logg = logging.getLogger()
# def transfer(
# self,
# token_address,
# receiver_address,
# Receiver_address,
# value,
# chain_spec,
# uuid,
@ -125,68 +132,68 @@ logg = logging.getLogger()
# 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_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)
}
#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)
@ -197,8 +204,8 @@ def balance(tokens, holder_address, chain_spec_dict):
:type tokens: list of str, 0x-hex
:param holder_address: Token holder address
:type holder_address: str, 0x-hex
:param chain_str: Chain spec string representation
:type chain_str: str
:param chain_spec_dict: Chain spec string representation
:type chain_spec_dict: str
:return: List of balances
:rtype: list of int
"""
@ -218,7 +225,7 @@ def balance(tokens, holder_address, chain_spec_dict):
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
def transfer(self, tokens, holder_address, receiver_address, value, chain_spec_dict):
"""Transfer ERC20 tokens between addresses
First argument is a list of tokens, to enable the task to be chained to the symbol to token address resolver function. However, it accepts only one token as argument.
@ -242,29 +249,31 @@ def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
# we only allow one token, one transfer
if len(tokens) != 1:
raise TokenCountError
chain_spec = ChainSpec.from_chain_str(chain_str)
queue = self.request.delivery_info['routing_key']
# retrieve the token interface
t = tokens[0]
chain_spec = ChainSpec.from_dict(chain_spec_dict)
queue = self.request.delivery_info.get('routing_key')
c = RpcClient(chain_spec, holder_address=holder_address)
registry = safe_registry(c.w3)
rpc = RPCConnection.connect(chain_spec, 'default')
rpc_signer = RPCConnection.connect(chain_spec, 'signer')
txf = TokenTxFactory(holder_address, c, registry=registry)
session = self.create_session()
nonce_oracle = CustodialTaskNonceOracle(holder_address, self.request.root_id, session=session)
gas_oracle = self.create_gas_oracle(rpc, MaxGasOracle.gas)
c = ERC20(signer=rpc_signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_spec.chain_id())
(tx_hash_hex, tx_signed_raw_hex) = c.transfer(t['address'], holder_address, receiver_address, value, tx_format=TxFormat.RLP_SIGNED)
cache_task = 'cic_eth.eth.erc20.cache_transfer_data'
session = SessionBase.create_session()
tx_transfer = txf.transfer(t['address'], receiver_address, value, chain_spec, self.request.root_id, session=session)
(tx_hash_hex, tx_signed_raw_hex) = sign_and_register_tx(tx_transfer, chain_str, queue, cache_task='cic_eth.eth.token.otx_cache_transfer', session=session)
register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=cache_task, session=session)
session.commit()
session.close()
gas_budget = tx_transfer['gas'] * tx_transfer['gasPrice']
gas_pair = gas_oracle.get_gas(tx_signed_raw_hex)
gas_budget = gas_pair[0] * gas_pair[1]
logg.debug('transfer tx {} {} {}'.format(tx_hash_hex, queue, gas_budget))
s = create_check_gas_and_send_task(
s = create_check_gas_task(
[tx_signed_raw_hex],
chain_str,
chain_spec,
holder_address,
gas_budget,
[tx_hash_hex],
@ -275,7 +284,7 @@ def transfer(self, tokens, holder_address, receiver_address, value, chain_str):
@celery_app.task(bind=True, base=CriticalSQLAlchemyAndSignerTask)
def approve(self, tokens, holder_address, spender_address, value, chain_str):
def approve(self, tokens, holder_address, spender_address, value, chain_spec_dict):
"""Approve ERC20 transfer on behalf of holder address
First argument is a list of tokens, to enable the task to be chained to the symbol to token address resolver function. However, it accepts only one token as argument.
@ -299,29 +308,30 @@ def approve(self, tokens, holder_address, spender_address, value, chain_str):
# we only allow one token, one transfer
if len(tokens) != 1:
raise TokenCountError
chain_spec = ChainSpec.from_chain_str(chain_str)
queue = self.request.delivery_info['routing_key']
# retrieve the token interface
t = tokens[0]
chain_spec = ChainSpec.from_dict(chain_spec_dict)
queue = self.request.delivery_info.get('routing_key')
c = RpcClient(chain_spec, holder_address=holder_address)
registry = safe_registry(c.w3)
rpc = RPCConnection.connect(chain_spec, 'default')
rpc_signer = RPCConnection.connect(chain_spec, 'signer')
txf = TokenTxFactory(holder_address, c, registry=registry)
session = self.create_session()
nonce_oracle = CustodialTaskNonceOracle(holder_address, self.request.root_id, session=session)
gas_oracle = self.create_gas_oracle(rpc, MaxGasOracle.gas)
c = ERC20(signer=rpc_signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_spec.chain_id())
(tx_hash_hex, tx_signed_raw_hex) = c.approve(t['address'], holder_address, spender_address, value, tx_format=TxFormat.RLP_SIGNED)
cache_task = 'cic_eth.eth.erc20.cache_approve_data'
session = SessionBase.create_session()
tx_transfer = txf.approve(t['address'], spender_address, value, chain_spec, self.request.root_id, session=session)
(tx_hash_hex, tx_signed_raw_hex) = sign_and_register_tx(tx_transfer, chain_str, queue, cache_task='cic_eth.eth.token.otx_cache_approve', session=session)
register_tx(tx_hash_hex, tx_signed_raw_hex, chain_spec, queue, cache_task=cache_task, session=session)
session.commit()
session.close()
gas_budget = tx_transfer['gas'] * tx_transfer['gasPrice']
gas_pair = gas_oracle.get_gas(tx_signed_raw_hex)
gas_budget = gas_pair[0] * gas_pair[1]
s = create_check_gas_and_send_task(
s = create_check_gas_task(
[tx_signed_raw_hex],
chain_str,
chain_spec,
holder_address,
gas_budget,
[tx_hash_hex],
@ -356,34 +366,11 @@ def resolve_tokens_by_symbol(token_symbols, chain_str):
return tokens
@celery_app.task(base=CriticalSQLAlchemyTask)
def otx_cache_transfer(
tx_hash_hex,
tx_signed_raw_hex,
chain_str,
):
"""Generates and commits transaction cache metadata for an ERC20.transfer or ERC20.transferFrom 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())
(txc, cache_id) = cache_transfer_data(tx_hash_hex, tx)
return txc
@celery_app.task(base=CriticalSQLAlchemyTask)
def cache_transfer_data(
tx_hash_hex,
tx,
tx_signed_raw_hex,
chain_spec_dict,
):
"""Helper function for otx_cache_transfer
@ -394,19 +381,23 @@ def cache_transfer_data(
:returns: Transaction hash and id of cache element in storage backend, respectively
:rtype: tuple
"""
tx_data = unpack_transfer(tx['data'])
logg.debug('tx data {}'.format(tx_data))
logg.debug('tx {}'.format(tx))
chain_spec = ChainSpec.from_dict(chain_spec_dict)
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
tx = unpack(tx_signed_raw_bytes, chain_spec.chain_id())
tx_data = ERC20.parse_transfer_request(tx['data'])
recipient_address = tx_data[0]
token_value = tx_data[1]
session = SessionBase.create_session()
tx_cache = TxCache(
tx_hash_hex,
tx['from'],
tx_data['to'],
recipient_address,
tx['to'],
tx['to'],
tx_data['amount'],
tx_data['amount'],
token_value,
token_value,
session=session,
)
session.add(tx_cache)
@ -416,34 +407,11 @@ def cache_transfer_data(
return (tx_hash_hex, cache_id)
@celery_app.task(base=CriticalSQLAlchemyTask)
def otx_cache_approve(
tx_hash_hex,
tx_signed_raw_hex,
chain_str,
):
"""Generates and commits transaction cache metadata for an ERC20.approve 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())
(txc, cache_id) = cache_approve_data(tx_hash_hex, tx)
return txc
@celery_app.task(base=CriticalSQLAlchemyTask)
def cache_approve_data(
tx_hash_hex,
tx,
tx_signed_raw_hex,
chain_spec_dict,
):
"""Helper function for otx_cache_approve
@ -454,19 +422,23 @@ def cache_approve_data(
:returns: Transaction hash and id of cache element in storage backend, respectively
:rtype: tuple
"""
tx_data = unpack_approve(tx['data'])
logg.debug('tx data {}'.format(tx_data))
logg.debug('tx {}'.format(tx))
chain_spec = ChainSpec.from_dict(chain_spec_dict)
tx_signed_raw_bytes = bytes.fromhex(strip_0x(tx_signed_raw_hex))
tx = unpack(tx_signed_raw_bytes, chain_spec.chain_id())
tx_data = ERC20.parse_approve_request(tx['data'])
recipient_address = tx_data[0]
token_value = tx_data[1]
session = SessionBase.create_session()
tx_cache = TxCache(
tx_hash_hex,
tx['from'],
tx_data['to'],
recipient_address,
tx['to'],
tx['to'],
tx_data['amount'],
tx_data['amount'],
token_value,
token_value,
session=session,
)
session.add(tx_cache)

View File

@ -12,77 +12,83 @@ from cic_eth.db.models.base import SessionBase
logg = logging.getLogger()
class GasOracle():
"""Provides gas pricing for transactions.
:param w3: Web3 object
:type w3: web3.Web3
"""
__safe_threshold_amount_value = 2000000000 * 60000 * 3
__refill_amount_value = __safe_threshold_amount_value * 5
default_gas_limit = 21000
def __init__(self, conn):
o = price()
r = conn.do(o)
b = bytes.from_hex(strip_0x(r))
self.gas_price_current = int.from_bytes(b, 'big')
#self.w3 = w3
#self.gas_price_current = w3.eth.gas_price()
#
#class GasOracle():
# """Provides gas pricing for transactions.
#
# :param w3: Web3 object
# :type w3: web3.Web3
# """
#
# __safe_threshold_amount_value = 2000000000 * 60000 * 3
# __refill_amount_value = __safe_threshold_amount_value * 5
# default_gas_limit = 21000
#
# def __init__(self, conn):
# o = price()
# r = conn.do(o)
# b = bytes.from_hex(strip_0x(r))
# self.gas_price_current = int.from_bytes(b, 'big')
#
# #self.w3 = w3
# #self.gas_price_current = w3.eth.gas_price()
#
#
# def safe_threshold_amount(self):
# """The gas balance threshold under which a new gas refill transaction should be initiated.
#
# :returns: Gas token amount
# :rtype: number
# """
# g = GasOracle.__safe_threshold_amount_value
# logg.warning('gas safe threshold is currently hardcoded to {}'.format(g))
# return g
#
#
# def refill_amount(self):
# """The amount of gas tokens to send in a gas refill transaction.
#
# :returns: Gas token amount
# :rtype: number
# """
# g = GasOracle.__refill_amount_value
# logg.warning('gas refill amount is currently hardcoded to {}'.format(g))
# return g
#
#
# def gas_provider(self):
# """Gas provider address.
#
# :returns: Etheerum account address
# :rtype: str, 0x-hex
# """
# session = SessionBase.create_session()
# a = AccountRole.get_address('GAS_GIFTER', session)
# logg.debug('gasgifter {}'.format(a))
# session.close()
# return a
#
#
# def gas_price(self, category='safe'):
# """Get projected gas price to use for a transaction at the current moment.
#
# When the category parameter is implemented, it can be used to control the priority of a transaction in the network.
#
# :param category: Bid level category to return price for. Currently has no effect.
# :type category: str
# :returns: Gas price
# :rtype: number
# """
# #logg.warning('gas price hardcoded to category "safe"')
# #g = 100
# #return g
# return self.gas_price_current
def safe_threshold_amount(self):
"""The gas balance threshold under which a new gas refill transaction should be initiated.
class MaxGasOracle:
:returns: Gas token amount
:rtype: number
"""
g = GasOracle.__safe_threshold_amount_value
logg.warning('gas safe threshold is currently hardcoded to {}'.format(g))
return g
def refill_amount(self):
"""The amount of gas tokens to send in a gas refill transaction.
:returns: Gas token amount
:rtype: number
"""
g = GasOracle.__refill_amount_value
logg.warning('gas refill amount is currently hardcoded to {}'.format(g))
return g
def gas_provider(self):
"""Gas provider address.
:returns: Etheerum account address
:rtype: str, 0x-hex
"""
session = SessionBase.create_session()
a = AccountRole.get_address('GAS_GIFTER', session)
logg.debug('gasgifter {}'.format(a))
session.close()
return a
def gas_price(self, category='safe'):
"""Get projected gas price to use for a transaction at the current moment.
When the category parameter is implemented, it can be used to control the priority of a transaction in the network.
:param category: Bid level category to return price for. Currently has no effect.
:type category: str
:returns: Gas price
:rtype: number
"""
#logg.warning('gas price hardcoded to category "safe"')
#g = 100
#return g
return self.gas_price_current
def gas(code=None):
return 8000000
def create_check_gas_task(tx_signed_raws_hex, chain_spec, holder_address, gas=None, tx_hashes_hex=None, queue=None):

View File

@ -1,39 +1,39 @@
# standard imports
import logging
# local imports
from cic_eth.eth.gas import GasOracle
logg = logging.getLogger()
class RpcClient(GasOracle):
"""RPC wrapper for web3 enabling gas calculation helpers and signer middleware.
class RpcClient:
pass
:param chain_spec: Chain spec
:type chain_spec: cic_registry.chain.ChainSpec
:param holder_address: DEPRECATED Address of subject of the session.
:type holder_address: str, 0x-hex
"""
signer_ipc_path = None
"""Unix socket path to JSONRPC signer and keystore"""
web3_constructor = None
"""Custom function to build a web3 object with middleware plugins"""
def __init__(self, chain_spec, holder_address=None):
(self.provider, w3) = RpcClient.web3_constructor()
super(RpcClient, self).__init__(w3)
self.chain_spec = chain_spec
if holder_address != None:
self.holder_address = holder_address
logg.info('gasprice {}'.format(self.gas_price()))
@staticmethod
def set_constructor(web3_constructor):
"""Sets the constructor to use for building the web3 object.
"""
RpcClient.web3_constructor = web3_constructor
#class RpcClient(GasOracle):
# """RPC wrapper for web3 enabling gas calculation helpers and signer middleware.
#
# :param chain_spec: Chain spec
# :type chain_spec: cic_registry.chain.ChainSpec
# :param holder_address: DEPRECATED Address of subject of the session.
# :type holder_address: str, 0x-hex
# """
#
# signer_ipc_path = None
# """Unix socket path to JSONRPC signer and keystore"""
#
# web3_constructor = None
# """Custom function to build a web3 object with middleware plugins"""
#
#
# def __init__(self, chain_spec, holder_address=None):
# (self.provider, w3) = RpcClient.web3_constructor()
# super(RpcClient, self).__init__(w3)
# self.chain_spec = chain_spec
# if holder_address != None:
# self.holder_address = holder_address
# logg.info('gasprice {}'.format(self.gas_price()))
#
#
# @staticmethod
# def set_constructor(web3_constructor):
# """Sets the constructor to use for building the web3 object.
# """
# RpcClient.web3_constructor = web3_constructor

View File

@ -1,7 +1,6 @@
pytest==6.0.1
pytest-celery==0.0.0a1
pytest-mock==3.3.1
py-eth==0.1.1
pytest-cov==2.10.1
eth-tester==0.5.0b3
py-evm==0.3.0a20

View File

@ -9,6 +9,7 @@ sys.path.insert(0, root_dir)
from tests.fixtures_config import *
from tests.fixtures_database import *
from tests.fixtures_celery import *
from tests.fixtures_role import *
from chainlib.eth.pytest import *
from contract_registry.pytest import *

View File

@ -11,6 +11,7 @@ logg = logging.getLogger()
# celery fixtures
@pytest.fixture(scope='session')
def celery_includes():
logg.debug('foooooooooooo ')
return [
# 'cic_eth.eth.bancor',
'cic_eth.eth.erc20',

View File

@ -18,14 +18,15 @@ logg = logging.getLogger()
@pytest.fixture(scope='function')
def custodial_roles(
contract_roles,
token_roles,
eth_accounts,
init_database,
):
r = {}
r.update(contract_roles)
r.update({
'DEFAULT': eth_accounts[0],
'GAS_GIFTER': eth_accounts[3],
'GAS_GIFTER': eth_accounts[10],
'FOO_TOKEN_GIFTER': token_roles['FOO_TOKEN_OWNER'],
})
for k in r.keys():
role = AccountRole.set(k, r[k])

View File

@ -1,22 +1,65 @@
# standard imports
import logging
# external imports
import pytest
import celery
from chainlib.eth.erc20 import ERC20
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.tx import receipt
from chainlib.eth.tx import (
receipt,
TxFormat,
)
# local imports
from cic_eth.queue.tx import register_tx
logg = logging.getLogger()
def test_erc20_balance(
def test_otx_cache_transfer(
default_chain_spec,
foo_token,
token_roles,
agent_roles,
eth_signer,
eth_rpc,
celery_worker,
init_database,
celery_session_worker,
):
nonce_oracle = RPCNonceOracle(token_roles['FOO_TOKEN_OWNER'], eth_rpc)
c = ERC20(signer=eth_signer, nonce_oracle=nonce_oracle, chain_id=default_chain_spec.chain_id())
transfer_value = 100 * (10**6)
(tx_hash_hex, tx_signed_raw_hex) = c.transfer(foo_token, token_roles['FOO_TOKEN_OWNER'], agent_roles['ALICE'], transfer_value, tx_format=TxFormat.RLP_SIGNED)
register_tx(tx_hash_hex, tx_signed_raw_hex, default_chain_spec, None, session=init_database)
s = celery.signature(
'cic_eth.eth.erc20.cache_transfer_data',
[
tx_hash_hex,
tx_signed_raw_hex,
default_chain_spec.asdict(),
],
queue=None,
)
t = s.apply_async()
r = t.get()
assert r[0] == tx_hash_hex
def test_erc20_balance_task(
default_chain_spec,
foo_token,
token_roles,
agent_roles,
eth_signer,
eth_rpc,
celery_session_worker,
):
nonce_oracle = RPCNonceOracle(token_roles['FOO_TOKEN_OWNER'], eth_rpc)
c = ERC20(signer=eth_signer, nonce_oracle=nonce_oracle)
c = ERC20(signer=eth_signer, nonce_oracle=nonce_oracle, chain_id=default_chain_spec.chain_id())
transfer_value = 100 * (10**6)
(tx_hash_hex, o) = c.transfer(foo_token, token_roles['FOO_TOKEN_OWNER'], agent_roles['ALICE'], transfer_value)
eth_rpc.do(o)
@ -41,3 +84,84 @@ def test_erc20_balance(
r = t.get()
assert r[0]['balance_network'] == transfer_value
def test_erc20_transfer_task(
default_chain_spec,
foo_token,
agent_roles,
custodial_roles,
eth_signer,
eth_rpc,
init_database,
celery_session_worker,
):
token_object = {
'address': foo_token,
}
transfer_value = 100 * (10 ** 6)
s_nonce = celery.signature(
'cic_eth.eth.tx.reserve_nonce',
[
[token_object],
custodial_roles['FOO_TOKEN_GIFTER'],
],
queue=None,
)
s_transfer = celery.signature(
'cic_eth.eth.erc20.transfer',
[
custodial_roles['FOO_TOKEN_GIFTER'],
agent_roles['ALICE'],
transfer_value,
default_chain_spec.asdict(),
],
queue=None,
)
s_nonce.link(s_transfer)
t = s_nonce.apply_async()
r = t.get_leaf()
logg.debug('result {}'.format(r))
def test_erc20_approve_task(
default_chain_spec,
foo_token,
agent_roles,
custodial_roles,
eth_signer,
eth_rpc,
init_database,
celery_session_worker,
):
token_object = {
'address': foo_token,
}
transfer_value = 100 * (10 ** 6)
s_nonce = celery.signature(
'cic_eth.eth.tx.reserve_nonce',
[
[token_object],
custodial_roles['FOO_TOKEN_GIFTER'],
],
queue=None,
)
s_transfer = celery.signature(
'cic_eth.eth.erc20.approve',
[
custodial_roles['FOO_TOKEN_GIFTER'],
agent_roles['ALICE'],
transfer_value,
default_chain_spec.asdict(),
],
queue=None,
)
s_nonce.link(s_transfer)
t = s_nonce.apply_async()
r = t.get_leaf()
logg.debug('result {}'.format(r))