Skip to content

Commit

Permalink
[HOTFIX-#238] Fix UDC Depositing.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nils Diefenbach authored Sep 6, 2019
2 parents 7db1a0e + 9dd1838 commit 167cb46
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 66 deletions.
9 changes: 7 additions & 2 deletions scenario_player/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,12 +308,16 @@ def _initialize_udc(
self.udc = UserDepositContract(self, udc_ctr, ud_token_ctr)

should_deposit_ud_token = udc_enabled and udc_settings.token["deposit"]
allowance_tx = self.udc.update_allowance()
allowance_tx, required_allowance = self.udc.update_allowance()
if allowance_tx:
ud_token_tx.add(allowance_tx)
if should_deposit_ud_token:

tx = self.udc.mint(our_address)
tx = self.udc.mint(
our_address,
required_balance=required_allowance,
max_fund_amount=required_allowance * 2,
)
if tx:
ud_token_tx.add(tx)

Expand All @@ -333,6 +337,7 @@ def _initialize_nodes(
for address, balance in balance_per_node.items()
if balance < NODE_ACCOUNT_BALANCE_MIN
}
log.debug("Node eth balances", balances=balance_per_node, low_balances=low_balances)
if low_balances:
log.info("Funding nodes", nodes=low_balances.keys())
fund_tx = set()
Expand Down
15 changes: 0 additions & 15 deletions scenario_player/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import yaml

from scenario_player.constants import GAS_LIMIT_FOR_TOKEN_CONTRACT_CALL
from scenario_player.exceptions.config import InsufficientMintingAmount
from scenario_player.utils.configuration import (
NodesConfig,
ScenarioConfig,
Expand Down Expand Up @@ -34,24 +33,10 @@ def __init__(self, yaml_path: pathlib.Path, data_path: pathlib.Path) -> None:
self.scenario = ScenarioConfig(self._loaded)
self.token = TokenConfig(self._loaded, data_path.joinpath("token.info"))
self.spaas = SPaaSConfig(self._loaded)
self.validate()

self.gas_limit = GAS_LIMIT_FOR_TOKEN_CONTRACT_CALL * 2

@property
def name(self) -> str:
"""Return the name of the scenario file, sans extension."""
return self.path.stem

def validate(self):
"""Validate cross-config section requirements of the scenario.
:raises InsufficientMintingAmount:
If token.min_balance < settings.services.udc.token.max_funding
"""

# Check that the amount of minted tokens is >= than the amount of deposited tokens
try:
assert self.token.min_balance >= self.settings.services.udc.token.max_funding
except AssertionError:
raise InsufficientMintingAmount
2 changes: 1 addition & 1 deletion scenario_player/services/rpc/blueprints/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def transact_call(key, data):

contract_proxy = rpc_client.new_contract_proxy(contract_abi, data["contract_address"])

log.debug("Transacting..", **data)
log.debug("Transacting..", action=action, **data)

args = data["amount"], data["target_address"]
if action != "mintFor":
Expand Down
5 changes: 5 additions & 0 deletions scenario_player/services/rpc/blueprints/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
"""
from flask import Blueprint, request
from structlog import get_logger

from scenario_player.services.common.metrics import REDMetricsTracker
from scenario_player.services.rpc.schemas.transactions import SendTransactionSchema

log = get_logger(__name__)


transactions_blueprint = Blueprint("transactions_view", __name__)


Expand Down Expand Up @@ -77,6 +81,7 @@ def new_transaction():
data = transaction_send_schema.validate_and_deserialize(request.get_json())
rpc_client, _ = data.pop("client"), data.pop("client_id")

log.debug("Performing transaction", params=data)
result = rpc_client.send_transaction(**data)

return transaction_send_schema.jsonify({"tx_hash": result})
98 changes: 68 additions & 30 deletions scenario_player/utils/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ def __init__(self, runner, address=None):
self.interface = ServiceInterface(runner.yaml.spaas)
self.gas_limit = GAS_LIMIT_FOR_TOKEN_CONTRACT_CALL * 2

def __repr__(self):
return f"<{self.name}>"

@property
def name(self):
return f"{self.__class__.__name__}@{to_checksum_address(self.address)}"

@property
def client_id(self):
return self.config.spaas.rpc.client_id
Expand Down Expand Up @@ -59,28 +66,35 @@ def transact(self, action: str, parameters: dict) -> str:
resp_data = resp.json()
tx_hash = resp_data["tx_hash"]
log.info(f"'{action}' call succeeded", tx_hash=tx_hash)
return decode_hex(tx_hash)
return decode_hex(tx_hash).decode()

def mint(self, target_address, **kwargs) -> Union[str, None]:
def mint(
self, target_address, required_balance=None, max_fund_amount=None, **kwargs
) -> Union[str, None]:
"""Mint new tokens for the given `target_address`.
The amount of tokens depends on the scenario yaml's settings, and defaults to
:attr:`.DEFAULT_TOKEN_BALANCE_MIN` and :attr:`.DEFAULT_TOKEN_BALANCE_FUND`
if those settings are absent.
"""
local_log = log.bind(contract=self.name)
balance = self.balance
required_balance = self.config.token.min_balance
log.debug(
if required_balance is None:
required_balance = self.config.token.min_balance
local_log.debug(
"Checking necessity of mint request",
required_balance=required_balance,
actual_balance=balance,
)
if not balance < required_balance:
log.debug("Mint call not required - sufficient funds")
local_log.debug("Mint call not required - sufficient funds")
return

mint_amount = self.config.token.max_funding - balance
log.debug("Minting required - insufficient funds.")
if max_fund_amount is None:
max_fund_amount = self.config.token.max_funding

mint_amount = max_fund_amount - balance
local_log.debug("Minting required - insufficient funds.", mint_amount=mint_amount)
params = {"amount": mint_amount, "target_address": target_address}
params.update(kwargs)
return self.transact("mint", params)
Expand All @@ -98,10 +112,11 @@ class Token(Contract):
"""

def __init__(self, scenario_runner, data_path: pathlib.Path):
super(Token, self).__init__(scenario_runner)
super().__init__(scenario_runner)
self._token_file = data_path.joinpath("token.info")
self.contract_data = {}
self.deployment_receipt = None
self.contract_proxy = None

@property
def name(self) -> str:
Expand Down Expand Up @@ -159,7 +174,7 @@ def balance(self) -> float:
It is an error to access this property before the token is deployed.
"""
if self.deployed:
return super(Token, self).balance
return self.contract_proxy.contract.functions.balanceOf(self.address).call()
else:
raise TokenNotDeployed

Expand Down Expand Up @@ -258,18 +273,24 @@ def use_existing(self) -> Tuple[str, int]:
f"Cannot reuse token - address {address} has no code stored!"
) from e

# Fetch the token's contract_proxy data.
contract_proxy = self._local_contract_manager.get_contract(contract_name)
# Fetch the token's contract_info data.
contract_info = self._local_contract_manager.get_contract(contract_name)

self.contract_data = {"token_contract": address, "name": contract_proxy.name}
self.contract_data = {
"token_contract": address,
"name": contract_info.get("name") or contract_name,
}
self.contract_proxy = self._local_rpc_client.new_contract_proxy(
contract_info["abi"], address
)
self.deployment_receipt = {"blockNum": block}
checksummed_address = to_checksum_address(address)

log.debug(
"Reusing token",
address=checksummed_address,
name=contract_proxy.name,
symbol=contract_proxy.symbol,
name=contract_name,
symbol=self.contract_proxy.contract.functions.symbol().call(),
)
return checksummed_address, block

Expand Down Expand Up @@ -307,9 +328,15 @@ def deploy_new(self) -> Tuple[str, int]:
resp_data["contract"],
resp_data["deployment_block"],
)
print(token_contract_data)
print(deployment_block)
contract_info = self._local_contract_manager.get_contract("CustomToken")

# Make deployment address and block available to address/deployment_block properties.
self.contract_data = token_contract_data
self.contract_proxy = self._local_rpc_client.new_contract_proxy(
contract_info["abi"], token_contract_data["address"]
)
self.deployment_receipt = {"blockNumber": deployment_block}

if self.config.token.reuse_token:
Expand All @@ -331,9 +358,7 @@ class UserDepositContract(Contract):
"""

def __init__(self, scenario_runner, contract_proxy, token_proxy):
super(UserDepositContract, self).__init__(
scenario_runner, address=contract_proxy.contract_address
)
super().__init__(scenario_runner, address=contract_proxy.contract_address)
self.contract_proxy = contract_proxy
self.token_proxy = token_proxy
self.tx_hashes = set()
Expand All @@ -349,6 +374,11 @@ def allowance(self):
self._local_rpc_client.address, self.address
).call()

@property
def balance(self):
"""Proxy the balance call to the UDTC."""
return self.token_proxy.contract.functions.balanceOf(self.ud_token_address).call()

def effective_balance(self, at_target):
"""Get the effective balance of the target address."""
return self.contract_proxy.contract.functions.effectiveBalance(at_target).call()
Expand All @@ -357,13 +387,19 @@ def total_deposit(self, at_target):
""""Get the so far deposted amount"""
return self.contract_proxy.contract.functions.total_deposit(at_target).call()

def mint(self, target_address) -> Union[str, None]:
def mint(
self, target_address, required_balance=None, max_fund_amount=None, **kwargs
) -> Union[str, None]:
"""The mint function isn't present on the UDC, pass the UDTC address instead."""
return super(UserDepositContract, self).mint(
target_address, contract_address=self.ud_token_address
return super().mint(
target_address,
required_balance=required_balance,
max_fund_amount=max_fund_amount,
contract_address=self.ud_token_address,
**kwargs,
)

def update_allowance(self) -> Union[str, None]:
def update_allowance(self) -> Union[Tuple[str, int], None]:
"""Update the UD Token Contract allowance depending on the number of configured nodes.
If the UD Token Contract's allowance is sufficient, this is a no-op.
Expand All @@ -373,23 +409,24 @@ def update_allowance(self) -> Union[str, None]:
required_allowance = self.config.settings.services.udc.token.balance_per_node * node_count

log.debug(
"Checking necessity of deposit request",
required_balance=required_allowance,
actual_balance=udt_allowance,
"Checking UDTC allowance",
required_allowance=required_allowance,
required_per_node=self.config.settings.services.udc.token.balance_per_node,
node_count=node_count,
actual_allowance=udt_allowance,
)

if not udt_allowance < required_allowance:
log.debug("allowance update call not required - sufficient allowance")
log.debug("UDTC allowance sufficient")
return

log.debug("allowance update call required - insufficient allowance")
allow_amount = required_allowance - udt_allowance
log.debug("UDTC allowance insufficient, updating")
params = {
"amount": allow_amount,
"amount": required_allowance,
"target_address": self.checksum_address,
"contract_address": self.ud_token_address,
}
return self.transact("allowance", params)
return self.transact("allowance", params), required_allowance

def deposit(self, target_address) -> Union[str, None]:
"""Make a deposit at the given `target_address`.
Expand All @@ -406,14 +443,15 @@ def deposit(self, target_address) -> Union[str, None]:
max_funding = self.config.settings.services.udc.token.max_funding
log.debug(
"Checking necessity of deposit request",
target_address=target_address,
required_balance=min_deposit,
actual_balance=balance,
)
if not balance < min_deposit:
log.debug("deposit call not required - sufficient funds")
return

log.debug("deposit call required - insufficient funds")
log.debug("deposit call required - insufficient funds", target_address=target_address)
deposit_amount = total_deposit + (max_funding - balance)
params = {"amount": deposit_amount, "target_address": target_address}
return self.transact("deposit", params)
6 changes: 0 additions & 6 deletions tests/unittests/utils/configuration/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,3 @@ def test_balance_per_node_must_not_be_greater_than_max_funding(self, minimal_yam
}
with pytest.raises(UDCTokenConfigError):
UDCTokenSettings(minimal_yaml_dict)

def test_insufficient_minting(self, file_for_insufficient_minting_test):
with pytest.raises(InsufficientMintingAmount):
ScenarioYAML(
file_for_insufficient_minting_test, file_for_insufficient_minting_test.parent
)
Loading

0 comments on commit 167cb46

Please sign in to comment.