Beispiel #1
0
    def _deposit_preconditions(
        self,
        beneficiary: Address,
        total_deposit: TokenAmount,
        given_block_identifier: BlockIdentifier,
        token: Token,
    ) -> Tuple[TokenAmount, TokenAmount]:
        try:
            previous_total_deposit = self.get_total_deposit(
                address=beneficiary, block_identifier=given_block_identifier
            )
            current_balance = token.balance_of(
                address=self.node_address, block_identifier=given_block_identifier
            )
            whole_balance = self.whole_balance(block_identifier=given_block_identifier)
            whole_balance_limit = self.whole_balance_limit(block_identifier=given_block_identifier)
        except ValueError:
            # If 'given_block_identifier' has been pruned, we cannot perform the
            # precondition checks but must still set the amount_to_deposit to a
            # reasonable value.
            previous_total_deposit = self.get_total_deposit(
                address=beneficiary, block_identifier=self.client.get_checking_block()
            )
            amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit)
        except BadFunctionCallOutput:
            raise_on_call_returned_empty(given_block_identifier)
        else:
            amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit)

            if whole_balance + amount_to_deposit > UINT256_MAX:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"The new deposit of {amount_to_deposit} would lead to an overflow."
                )
                raise BrokenPreconditionError(msg)

            if whole_balance + amount_to_deposit > whole_balance_limit:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"With the new deposit of {amount_to_deposit}, the deposit "
                    f"limit of {whole_balance_limit} would be exceeded."
                )
                raise BrokenPreconditionError(msg)

            if total_deposit <= previous_total_deposit:
                msg = (
                    f"Current total deposit {previous_total_deposit} is already larger "
                    f"than the requested total deposit amount {total_deposit}"
                )
                raise BrokenPreconditionError(msg)

            if current_balance < amount_to_deposit:
                msg = (
                    f"new_total_deposit - previous_total_deposit = {amount_to_deposit} "
                    f"can not be larger than the available balance {current_balance}, "
                    f"for token at address {to_checksum_address(token.address)}"
                )
                raise BrokenPreconditionError(msg)

        return previous_total_deposit, amount_to_deposit
Beispiel #2
0
    def _check_why_deposit_failed(
        self,
        token: Token,
        amount_to_deposit: TokenAmount,
        total_deposit: TokenAmount,
        block_identifier: BlockSpecification,
    ) -> str:
        msg = ""
        latest_deposit = self.get_total_deposit(
            address=self.node_address, block_identifier=block_identifier)

        allowance = token.allowance(
            owner=self.node_address,
            spender=Address(self.address),
            block_identifier=block_identifier,
        )
        if allowance < amount_to_deposit:
            msg = ("The allowance is insufficient. Check concurrent deposits "
                   "for the same token network but different proxies.")
        elif token.balance_of(self.node_address,
                              block_identifier) < amount_to_deposit:
            msg = "The address doesnt have enough tokens"
        elif latest_deposit < total_deposit:
            msg = "Deposit amount did not increase after deposit transaction"
        else:
            msg = "Deposit failed of unknown reason"

        return msg
Beispiel #3
0
    def _check_why_deposit_failed(
        self,
        token: Token,
        amount_to_deposit: TokenAmount,
        total_deposit: TokenAmount,
        block_identifier: BlockSpecification,
    ) -> Tuple[Union[RaidenRecoverableError, RaidenUnrecoverableError], str, ]:
        error_type = RaidenUnrecoverableError
        msg = ''
        latest_deposit = self.get_total_deposit(
            address=self.node_address,
            block_identifier=block_identifier,
        )

        allowance = token.allowance(
            owner=self.node_address,
            spender=Address(self.address),
            block_identifier=block_identifier,
        )
        if allowance < amount_to_deposit:
            msg = ('The allowance is insufficient. Check concurrent deposits '
                   'for the same token network but different proxies.')
        elif token.balance_of(self.node_address,
                              block_identifier) < amount_to_deposit:
            msg = 'The address doesnt have enough tokens'
        elif latest_deposit < total_deposit:
            msg = 'Deposit amount did not increase after deposit transaction'
        else:
            msg = 'Deposit failed of unknown reason'

        return error_type, msg
Beispiel #4
0
    def _withdraw_check_result(
        self,
        transaction_sent: Optional[TransactionSent],
        amount_to_withdraw: TokenAmount,
        token: Token,
        previous_token_balance: TokenAmount,
    ) -> None:
        if transaction_sent is None:
            failed_at = self.client.get_block(BLOCK_ID_LATEST)
            failed_at_blocknumber = failed_at["number"]

            self.client.check_for_insufficient_eth(
                transaction_name="withdraw",
                transaction_executed=False,
                required_gas=self.gas_measurements["UserDeposit.withdraw"],
                block_identifier=failed_at_blocknumber,
            )
            raise RaidenRecoverableError(
                "Withdraw transaction failed to be sent for an unknown reason."
            )

        transaction_mined = self.client.poll_transaction(transaction_sent)

        if not was_transaction_successfully_mined(transaction_mined):
            failed_at_blocknumber = BlockNumber(transaction_mined.receipt["blockNumber"])

            withdraw_plan = self.get_withdraw_plan(
                withdrawer_address=self.node_address, block_identifier=failed_at_blocknumber
            )
            whole_balance = self.whole_balance(block_identifier=failed_at_blocknumber)

            if amount_to_withdraw > withdraw_plan.withdraw_amount:
                raise RaidenRecoverableError(
                    f"Couldn't withdraw {amount_to_withdraw}, "
                    f"current withdraw plan only allows for {withdraw_plan.withdraw_amount}."
                )

            if withdraw_plan.withdraw_block > failed_at_blocknumber:
                raise RaidenRecoverableError(
                    f"Couldn't withdraw at block {failed_at_blocknumber}. "
                    f"The current withdraw plan requires block number "
                    f"{withdraw_plan.withdraw_block}."
                )

            if whole_balance - amount_to_withdraw < 0:
                raise RaidenRecoverableError(
                    f"The current whole balance is {whole_balance}. "
                    f"The withdraw of {amount_to_withdraw} would have lead to an underflow."
                )

            current_token_balance = TokenAmount(
                token.balance_of(self.node_address, block_identifier=failed_at_blocknumber)
            )
            # FIXME: This seems fishy. The token balance could have an unexpected value for
            #        unrelated reasons (e.g. concurrent token transfers via transferFrom).
            if current_token_balance != previous_token_balance + amount_to_withdraw:
                raise RaidenRecoverableError("Token transfer during withdraw failed.")

            raise RaidenRecoverableError("Withdraw failed for an unknown reason.")
Beispiel #5
0
    def deposit(self, amount):
        """ Deposit amount token in the channel.

        Raises:
            AddressWithoutCode: If the channel was settled prior to the call.
            RuntimeError: If the netting channel token address is empty.
        """
        if not isinstance(amount, (int, long)):
            raise ValueError('amount needs to be an integral number.')

        token_address = self.token_address()

        token = Token(
            self.client,
            token_address,
            self.startgas,
            self.gasprice,
            self.poll_timeout,
        )
        current_balance = token.balance_of(self.node_address)

        if current_balance < amount:
            raise ValueError(
                'deposit [{}] cant be larger than the available balance [{}].'.
                format(
                    amount,
                    current_balance,
                ))

        if log.isEnabledFor(logging.INFO):
            log.info('deposit called',
                     contract=pex(self.address),
                     amount=amount)

        transaction_hash = estimate_and_transact(
            self.proxy,
            'deposit',
            self.startgas,
            self.gasprice,
            amount,
        )

        self.client.poll(
            unhexlify(transaction_hash),
            timeout=self.poll_timeout,
        )

        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)
        if receipt_or_none:
            log.critical('deposit failed', contract=pex(self.address))
            self._check_exists()
            raise TransactionThrew('Deposit', receipt_or_none)

        if log.isEnabledFor(logging.INFO):
            log.info('deposit sucessfull',
                     contract=pex(self.address),
                     amount=amount)
Beispiel #6
0
    def _deposit_preconditions(
        self,
        total_deposit: TokenAmount,
        beneficiary: Address,
        token: Token,
        block_identifier: BlockSpecification,
    ) -> Tuple[TokenAmount, Dict]:
        if not isinstance(total_deposit, int):
            raise ValueError('total_deposit needs to be an integer number.')

        previous_total_deposit = self.get_total_deposit(
            address=beneficiary,
            block_identifier=block_identifier,
        )
        amount_to_deposit = total_deposit - previous_total_deposit

        log_details = {
            'user_deposit_address': pex(self.address),
            'node': pex(self.node_address),
            'beneficiary': pex(beneficiary),
            'new_total_deposit': total_deposit,
            'previous_total_deposit': previous_total_deposit,
        }

        if total_deposit <= previous_total_deposit:
            msg = (
                f'Current total deposit {previous_total_deposit} is already larger '
                f'than the requested total deposit amount {total_deposit}')
            log.info('deposit failed', reason=msg, **log_details)
            raise DepositMismatch(msg)

        current_balance = token.balance_of(
            address=self.node_address,
            block_identifier=block_identifier,
        )
        if current_balance < amount_to_deposit:
            msg = (
                f'new_total_deposit - previous_total_deposit =  {amount_to_deposit} can not '
                f'be larger than the available balance {current_balance}, '
                f'for token at address {pex(token.address)}')
            log.info('deposit failed', reason=msg, **log_details)
            raise DepositMismatch(msg)

        token.approve(
            allowed_address=Address(self.address),
            allowance=amount_to_deposit,
        )

        return amount_to_deposit, log_details
Beispiel #7
0
    def _deposit_preconditions(
        self,
        total_deposit: TokenAmount,
        beneficiary: Address,
        token: Token,
        block_identifier: BlockSpecification,
    ) -> Tuple[TokenAmount, Dict]:
        if not isinstance(total_deposit, int):
            raise ValueError("total_deposit needs to be an integer number.")

        previous_total_deposit = self.get_total_deposit(
            address=beneficiary, block_identifier=block_identifier
        )
        amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit)

        log_details = {
            "user_deposit_address": pex(self.address),
            "node": pex(self.node_address),
            "beneficiary": pex(beneficiary),
            "new_total_deposit": total_deposit,
            "previous_total_deposit": previous_total_deposit,
        }

        if total_deposit <= previous_total_deposit:
            msg = (
                f"Current total deposit {previous_total_deposit} is already larger "
                f"than the requested total deposit amount {total_deposit}"
            )
            log.info("deposit failed", reason=msg, **log_details)
            raise DepositMismatch(msg)

        current_balance = token.balance_of(
            address=self.node_address, block_identifier=block_identifier
        )
        if current_balance < amount_to_deposit:
            msg = (
                f"new_total_deposit - previous_total_deposit =  {amount_to_deposit} can not "
                f"be larger than the available balance {current_balance}, "
                f"for token at address {pex(token.address)}"
            )
            log.info("deposit failed", reason=msg, **log_details)
            raise DepositMismatch(msg)

        token.approve(allowed_address=Address(self.address), allowance=amount_to_deposit)

        return amount_to_deposit, log_details
Beispiel #8
0
    def _deposit(
        self,
        beneficiary: Address,
        token: Token,
        total_deposit: TokenAmount,
        amount_to_deposit: TokenAmount,
        log_details: Dict[str, Any],
    ) -> None:
        token.approve(allowed_address=Address(self.address),
                      allowance=amount_to_deposit)

        checking_block = self.client.get_checking_block()
        gas_limit = self.proxy.estimate_gas(checking_block, "deposit",
                                            to_checksum_address(beneficiary),
                                            total_deposit)

        if not gas_limit:
            failed_at = self.proxy.rpc_client.get_block("latest")
            failed_at_blocknumber = failed_at["number"]

            self.proxy.rpc_client.check_for_insufficient_eth(
                transaction_name="deposit",
                transaction_executed=False,
                required_gas=self.gas_measurements["UserDeposit.deposit"],
                block_identifier=failed_at_blocknumber,
            )

            latest_deposit = self.get_total_deposit(
                address=self.node_address,
                block_identifier=failed_at_blocknumber)
            amount_to_deposit = TokenAmount(total_deposit - latest_deposit)

            allowance = token.allowance(
                owner=self.node_address,
                spender=Address(self.address),
                block_identifier=failed_at_blocknumber,
            )
            whole_balance = self.whole_balance(
                block_identifier=failed_at_blocknumber)
            whole_balance_limit = self.whole_balance_limit(
                block_identifier=failed_at_blocknumber)

            if allowance < amount_to_deposit:
                msg = (
                    "The allowance is insufficient. Check concurrent deposits "
                    "for the same user deposit but different proxies.")
                raise RaidenRecoverableError(msg)

            if token.balance_of(self.node_address,
                                failed_at_blocknumber) < amount_to_deposit:
                msg = "The address doesnt have enough tokens"
                raise RaidenRecoverableError(msg)

            if latest_deposit < total_deposit:
                msg = "Deposit amount did not increase after deposit transaction"
                raise RaidenRecoverableError(msg)

            if whole_balance + amount_to_deposit > UINT256_MAX:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"The new deposit of {amount_to_deposit} would lead to an overflow."
                )
                raise RaidenRecoverableError(msg)

            if whole_balance + amount_to_deposit > whole_balance_limit:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"With the new deposit of {amount_to_deposit}, the deposit "
                    f"limit of {whole_balance_limit} would be exceeded.")
                raise RaidenRecoverableError(msg)

            raise RaidenRecoverableError("Deposit failed of unknown reason")

        else:
            gas_limit = safe_gas_limit(gas_limit)
            log_details["gas_limit"] = gas_limit

            transaction_hash = self.proxy.transact(
                "deposit", gas_limit, to_checksum_address(beneficiary),
                total_deposit)

            receipt = self.client.poll(transaction_hash)
            failed_receipt = check_transaction_threw(receipt=receipt)

            if failed_receipt:
                failed_at_blocknumber = failed_receipt["blockNumber"]

                latest_deposit = self.get_total_deposit(
                    address=self.node_address,
                    block_identifier=failed_at_blocknumber)
                amount_to_deposit = TokenAmount(total_deposit - latest_deposit)

                allowance = token.allowance(
                    owner=self.node_address,
                    spender=Address(self.address),
                    block_identifier=failed_at_blocknumber,
                )

                whole_balance = self.whole_balance(
                    block_identifier=failed_at_blocknumber)
                whole_balance_limit = self.whole_balance_limit(
                    block_identifier=failed_at_blocknumber)

                if latest_deposit >= total_deposit:
                    msg = "Deposit amount already increased after another transaction"
                    raise RaidenRecoverableError(msg)

                if allowance < amount_to_deposit:
                    msg = (
                        "The allowance is insufficient. Check concurrent deposits "
                        "for the same token network but different proxies.")
                    raise RaidenRecoverableError(msg)

                # Because we acquired the lock for the token, and the gas estimation succeeded,
                # We know that the account had enough balance for the deposit transaction.
                if token.balance_of(self.node_address,
                                    failed_at_blocknumber) < amount_to_deposit:
                    msg = (
                        f"Transaction failed and balance decreased unexpectedly. "
                        f"This could be a bug in Raiden or a mallicious "
                        f"ERC20 Token.")
                    raise RaidenRecoverableError(msg)

                if whole_balance + amount_to_deposit > UINT256_MAX:
                    msg = (
                        f"Current whole balance is {whole_balance}. "
                        f"The new deposit of {amount_to_deposit} caused an overflow."
                    )
                    raise RaidenRecoverableError(msg)

                if whole_balance + amount_to_deposit > whole_balance_limit:
                    msg = (
                        f"Current whole balance is {whole_balance}. "
                        f"With the new deposit of {amount_to_deposit}, the deposit "
                        f"limit of {whole_balance_limit} was exceeded.")
                    raise RaidenRecoverableError(msg)

                if latest_deposit < total_deposit:
                    msg = "Deposit amount did not increase after deposit transaction"
                    raise RaidenRecoverableError(msg)

                raise RaidenRecoverableError(
                    "Deposit failed of unknown reason")
Beispiel #9
0
def test_payment_channel_proxy_basics(
    token_network_registry_address: TokenNetworkRegistryAddress,
    token_network_proxy: TokenNetwork,
    token_proxy: Token,
    chain_id: ChainID,
    private_keys: List[PrivateKey],
    web3: Web3,
    contract_manager: ContractManager,
    reveal_timeout: BlockTimeout,
) -> None:
    token_network_address = token_network_proxy.address
    partner = privatekey_to_address(private_keys[0])

    rpc_client = JSONRPCClient(web3, private_keys[1])
    proxy_manager = ProxyManager(
        rpc_client=rpc_client,
        contract_manager=contract_manager,
        metadata=ProxyManagerMetadata(
            token_network_registry_deployed_at=GENESIS_BLOCK_NUMBER,
            filters_start_at=GENESIS_BLOCK_NUMBER,
        ),
    )
    token_network_proxy = proxy_manager.token_network(
        address=token_network_address, block_identifier=BLOCK_ID_LATEST
    )
    start_block = web3.eth.blockNumber

    channel_details = token_network_proxy.new_netting_channel(
        partner=partner,
        settle_timeout=TEST_SETTLE_TIMEOUT_MIN,
        given_block_identifier=BLOCK_ID_LATEST,
    )
    channel_identifier = channel_details.channel_identifier
    assert channel_identifier is not None

    channel_state = NettingChannelState(
        canonical_identifier=CanonicalIdentifier(
            chain_identifier=chain_id,
            token_network_address=token_network_address,
            channel_identifier=channel_identifier,
        ),
        token_address=token_network_proxy.token_address(),
        token_network_registry_address=token_network_registry_address,
        reveal_timeout=reveal_timeout,
        settle_timeout=BlockTimeout(TEST_SETTLE_TIMEOUT_MIN),
        fee_schedule=FeeScheduleState(),
        our_state=NettingChannelEndState(
            address=token_network_proxy.client.address, contract_balance=Balance(0)
        ),
        partner_state=NettingChannelEndState(address=partner, contract_balance=Balance(0)),
        open_transaction=SuccessfulTransactionState(finished_block_number=BlockNumber(0)),
    )
    channel_proxy_1 = proxy_manager.payment_channel(
        channel_state=channel_state, block_identifier=BLOCK_ID_LATEST
    )

    assert channel_proxy_1.channel_identifier == channel_identifier
    assert channel_proxy_1.opened(BLOCK_ID_LATEST) is True

    # Test deposit
    initial_token_balance = 100
    token_proxy.transfer(rpc_client.address, TokenAmount(initial_token_balance))
    assert token_proxy.balance_of(rpc_client.address) == initial_token_balance
    assert token_proxy.balance_of(partner) == 0
    channel_proxy_1.approve_and_set_total_deposit(
        total_deposit=TokenAmount(10), block_identifier=BLOCK_ID_LATEST
    )

    # ChannelOpened, ChannelNewDeposit
    channel_events = get_all_netting_channel_events(
        proxy_manager=proxy_manager,
        token_network_address=token_network_address,
        netting_channel_identifier=channel_proxy_1.channel_identifier,
        contract_manager=contract_manager,
        from_block=start_block,
        to_block=web3.eth.blockNumber,
    )

    assert len(channel_events) == 2

    block_before_close = web3.eth.blockNumber
    empty_balance_proof = BalanceProof(
        channel_identifier=channel_proxy_1.channel_identifier,
        token_network_address=token_network_address,
        balance_hash=EMPTY_BALANCE_HASH,
        nonce=0,
        chain_id=chain_id,
        transferred_amount=TokenAmount(0),
    )
    closing_data = (
        empty_balance_proof.serialize_bin(msg_type=MessageTypeId.BALANCE_PROOF) + EMPTY_SIGNATURE
    )
    channel_proxy_1.close(
        nonce=Nonce(0),
        balance_hash=EMPTY_BALANCE_HASH,
        additional_hash=EMPTY_MESSAGE_HASH,
        non_closing_signature=EMPTY_SIGNATURE,
        closing_signature=LocalSigner(private_keys[1]).sign(data=closing_data),
        block_identifier=BLOCK_ID_LATEST,
    )
    assert channel_proxy_1.closed(BLOCK_ID_LATEST) is True
    # ChannelOpened, ChannelNewDeposit, ChannelClosed
    channel_events = get_all_netting_channel_events(
        proxy_manager=proxy_manager,
        token_network_address=token_network_address,
        netting_channel_identifier=channel_proxy_1.channel_identifier,
        contract_manager=contract_manager,
        from_block=start_block,
        to_block=web3.eth.blockNumber,
    )
    assert len(channel_events) == 3

    # check the settlement timeouts again
    assert channel_proxy_1.settle_timeout() == TEST_SETTLE_TIMEOUT_MIN

    # update transfer -- we need to wait on +1 since we use the latest block on parity for
    # estimate gas and at the time the latest block is the settle timeout block.
    # More info: https://github.com/raiden-network/raiden/pull/3699#discussion_r270477227
    rpc_client.wait_until_block(
        target_block_number=BlockNumber(rpc_client.block_number() + TEST_SETTLE_TIMEOUT_MIN + 1)
    )

    transaction_hash = channel_proxy_1.settle(
        transferred_amount=TokenAmount(0),
        locked_amount=LockedAmount(0),
        locksroot=LOCKSROOT_OF_NO_LOCKS,
        partner_transferred_amount=TokenAmount(0),
        partner_locked_amount=LockedAmount(0),
        partner_locksroot=LOCKSROOT_OF_NO_LOCKS,
        block_identifier=BLOCK_ID_LATEST,
    )
    assert is_tx_hash_bytes(transaction_hash)
    assert channel_proxy_1.settled(BLOCK_ID_LATEST) is True
    # ChannelOpened, ChannelNewDeposit, ChannelClosed, ChannelSettled
    channel_events = get_all_netting_channel_events(
        proxy_manager=proxy_manager,
        token_network_address=token_network_address,
        netting_channel_identifier=channel_proxy_1.channel_identifier,
        contract_manager=contract_manager,
        from_block=start_block,
        to_block=web3.eth.blockNumber,
    )
    assert len(channel_events) == 4

    channel_details = token_network_proxy.new_netting_channel(
        partner=partner,
        settle_timeout=TEST_SETTLE_TIMEOUT_MIN,
        given_block_identifier=BLOCK_ID_LATEST,
    )
    new_channel_identifier = channel_details.channel_identifier
    assert new_channel_identifier is not None

    channel_state = NettingChannelState(
        canonical_identifier=CanonicalIdentifier(
            chain_identifier=chain_id,
            token_network_address=token_network_address,
            channel_identifier=new_channel_identifier,
        ),
        token_address=token_network_proxy.token_address(),
        token_network_registry_address=token_network_registry_address,
        reveal_timeout=reveal_timeout,
        settle_timeout=BlockTimeout(TEST_SETTLE_TIMEOUT_MIN),
        fee_schedule=FeeScheduleState(),
        our_state=NettingChannelEndState(
            address=token_network_proxy.client.address, contract_balance=Balance(0)
        ),
        partner_state=NettingChannelEndState(address=partner, contract_balance=Balance(0)),
        open_transaction=SuccessfulTransactionState(finished_block_number=BlockNumber(0)),
    )
    channel_proxy_2 = proxy_manager.payment_channel(
        channel_state=channel_state, block_identifier=BLOCK_ID_LATEST
    )

    assert channel_proxy_2.channel_identifier == new_channel_identifier
    assert channel_proxy_2.opened(BLOCK_ID_LATEST) is True

    msg = "The channel was already closed, the second call must fail"
    with pytest.raises(RaidenRecoverableError):
        channel_proxy_1.close(
            nonce=Nonce(0),
            balance_hash=EMPTY_BALANCE_HASH,
            additional_hash=EMPTY_MESSAGE_HASH,
            non_closing_signature=EMPTY_SIGNATURE,
            closing_signature=LocalSigner(private_keys[1]).sign(data=closing_data),
            block_identifier=block_before_close,
        )
        pytest.fail(msg)

    msg = "The channel is not open at latest, this must raise"
    with pytest.raises(RaidenUnrecoverableError):
        channel_proxy_1.close(
            nonce=Nonce(0),
            balance_hash=EMPTY_BALANCE_HASH,
            additional_hash=EMPTY_MESSAGE_HASH,
            non_closing_signature=EMPTY_SIGNATURE,
            closing_signature=LocalSigner(private_keys[1]).sign(data=closing_data),
            block_identifier=BLOCK_ID_LATEST,
        )
        pytest.fail(msg)

    msg = (
        "The channel was not opened at the provided block (latest). "
        "This call should never have been attempted."
    )
    with pytest.raises(BrokenPreconditionError):
        channel_proxy_1.approve_and_set_total_deposit(
            total_deposit=TokenAmount(20), block_identifier=BLOCK_ID_LATEST
        )
        pytest.fail(msg)
Beispiel #10
0
    def deposit(self, total_deposit: typing.TokenAmount, partner: typing.Address):
        """ Set total token deposit in the channel to total_deposit.

        Raises:
            ChannelBusyError: If the channel is busy with another operation
            RuntimeError: If the token address is empty.
        """
        if not isinstance(total_deposit, int):
            raise ValueError('total_deposit needs to be an integral number.')

        token_address = self.token_address()

        token = Token(
            self.client,
            token_address,
            self.poll_timeout,
        )
        current_balance = token.balance_of(self.node_address)
        current_deposit = self.detail_participant(self.node_address, partner)['deposit']
        amount_to_deposit = total_deposit - current_deposit

        if amount_to_deposit <= 0:
            raise ValueError('deposit [{}] must be greater than 0.'.format(
                amount_to_deposit,
            ))

        if current_balance < amount_to_deposit:
            raise ValueError(
                f'deposit {amount_to_deposit} cant be larger than the '
                f'available balance {current_balance}, '
                f'for token at address {pex(token_address)}',
            )

        log.info(
            'deposit called',
            token_network=pex(self.address),
            node=pex(self.node_address),
            partner=pex(partner),
            total_deposit=total_deposit,
            amount_to_deposit=amount_to_deposit,
        )

        self._check_channel_lock(partner)

        with releasing(self.channel_operations_lock[partner]):
            transaction_hash = self.proxy.transact(
                'setTotalDeposit',
                self.node_address,
                total_deposit,
                partner,
            )

            self.client.poll(
                unhexlify(transaction_hash),
                timeout=self.poll_timeout,
            )

            receipt_or_none = check_transaction_threw(self.client, transaction_hash)
            if receipt_or_none:
                log.critical(
                    'deposit failed',
                    token_network=pex(self.address),
                    node=pex(self.node_address),
                    partner=pex(partner),
                    total_deposit=total_deposit,
                )

                channel_opened = self.channel_is_opened(partner)
                if channel_opened is False:
                    raise ChannelIncorrectStateError(
                        'Channel is not in an opened state. A deposit cannot be made',
                    )
                raise TransactionThrew('Deposit', receipt_or_none)

            log.info(
                'deposit successful',
                token_network=pex(self.address),
                node=pex(self.node_address),
                partner=pex(partner),
                total_deposit=total_deposit,
            )
Beispiel #11
0
    def _deposit_check_result(
        self,
        transaction_sent: Optional[TransactionSent],
        token: Token,
        beneficiary: Address,
        total_deposit: TokenAmount,
        amount_to_deposit: TokenAmount,
    ) -> None:
        if transaction_sent is None:
            failed_at = self.client.get_block(BLOCK_ID_LATEST)
            failed_at_blocknumber = failed_at["number"]

            self.client.check_for_insufficient_eth(
                transaction_name="deposit",
                transaction_executed=False,
                required_gas=self.gas_measurements["UserDeposit.deposit"],
                block_identifier=failed_at_blocknumber,
            )

            latest_deposit = self.get_total_deposit(
                address=beneficiary, block_identifier=failed_at_blocknumber
            )
            amount_to_deposit = TokenAmount(total_deposit - latest_deposit)

            allowance = token.allowance(
                owner=self.node_address,
                spender=Address(self.address),
                block_identifier=failed_at_blocknumber,
            )
            whole_balance = self.whole_balance(block_identifier=failed_at_blocknumber)
            whole_balance_limit = self.whole_balance_limit(block_identifier=failed_at_blocknumber)

            if allowance < amount_to_deposit:
                msg = (
                    "The allowance is insufficient. Check concurrent deposits "
                    "for the same user deposit but different proxies."
                )
                raise RaidenRecoverableError(msg)

            if token.balance_of(self.node_address, failed_at_blocknumber) < amount_to_deposit:
                msg = "The address doesnt have enough tokens"
                raise RaidenRecoverableError(msg)

            if latest_deposit < total_deposit:
                msg = "Deposit amount did not increase after deposit transaction"
                raise RaidenRecoverableError(msg)

            if whole_balance + amount_to_deposit > UINT256_MAX:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"The new deposit of {amount_to_deposit} would lead to an overflow."
                )
                raise RaidenRecoverableError(msg)

            if whole_balance + amount_to_deposit > whole_balance_limit:
                msg = (
                    f"Current whole balance is {whole_balance}. "
                    f"With the new deposit of {amount_to_deposit}, the deposit "
                    f"limit of {whole_balance_limit} would be exceeded."
                )
                raise RaidenRecoverableError(msg)

            raise RaidenRecoverableError("Deposit failed of unknown reason")

        else:
            transaction_mined = self.client.poll_transaction(transaction_sent)

            if not was_transaction_successfully_mined(transaction_mined):
                failed_at_blocknumber = BlockNumber(transaction_mined.receipt["blockNumber"])

                latest_deposit = self.get_total_deposit(
                    address=beneficiary, block_identifier=failed_at_blocknumber
                )
                amount_to_deposit = TokenAmount(total_deposit - latest_deposit)

                allowance = token.allowance(
                    owner=self.node_address,
                    spender=Address(self.address),
                    block_identifier=failed_at_blocknumber,
                )

                whole_balance = self.whole_balance(block_identifier=failed_at_blocknumber)
                whole_balance_limit = self.whole_balance_limit(
                    block_identifier=failed_at_blocknumber
                )

                if latest_deposit >= total_deposit:
                    msg = "Deposit amount already increased after another transaction"
                    raise RaidenRecoverableError(msg)

                if allowance < amount_to_deposit:
                    msg = (
                        "The allowance is insufficient. Check concurrent deposits "
                        "for the same token network but different proxies."
                    )
                    raise RaidenRecoverableError(msg)

                # Because we acquired the lock for the token, and the gas estimation succeeded,
                # We know that the account had enough balance for the deposit transaction.
                if token.balance_of(self.node_address, failed_at_blocknumber) < amount_to_deposit:
                    msg = (
                        "Transaction failed and balance decreased unexpectedly. "
                        "This could be a bug in Raiden or a mallicious "
                        "ERC20 Token."
                    )
                    raise RaidenRecoverableError(msg)

                if whole_balance + amount_to_deposit > UINT256_MAX:
                    msg = (
                        f"Current whole balance is {whole_balance}. "
                        f"The new deposit of {amount_to_deposit} caused an overflow."
                    )
                    raise RaidenRecoverableError(msg)

                if whole_balance + amount_to_deposit > whole_balance_limit:
                    msg = (
                        f"Current whole balance is {whole_balance}. "
                        f"With the new deposit of {amount_to_deposit}, the deposit "
                        f"limit of {whole_balance_limit} was exceeded."
                    )
                    raise RaidenRecoverableError(msg)

                if latest_deposit < total_deposit:
                    msg = "Deposit amount did not increase after deposit transaction"
                    raise RaidenRecoverableError(msg)

                raise RaidenRecoverableError("Deposit failed of unknown reason")
Beispiel #12
0
    def deposit(self, amount):
        """ Deposit amount token in the channel.

        Raises:
            AddressWithoutCode: If the channel was settled prior to the call.
            ChannelBusyError: If the channel is busy with another operation
            RuntimeError: If the netting channel token address is empty.
        """
        if not isinstance(amount, int):
            raise ValueError('amount needs to be an integral number.')

        token_address = self.token_address()

        token = Token(
            self.client,
            token_address,
            self.poll_timeout,
        )
        current_balance = token.balance_of(self.node_address)

        if current_balance < amount:
            raise ValueError('deposit [{}] cant be larger than the available balance [{}].'.format(
                amount,
                current_balance,
            ))

        log.info(
            'deposit called',
            node=pex(self.node_address),
            contract=pex(self.address),
            amount=amount,
        )

        if not self.channel_operations_lock.acquire(blocking=False):
            raise ChannelBusyError(
                f'Channel with address {self.address} is '
                f'busy with another ongoing operation.'
            )

        with releasing(self.channel_operations_lock):
            transaction_hash = estimate_and_transact(
                self.proxy,
                'deposit',
                amount,
            )

            self.client.poll(
                unhexlify(transaction_hash),
                timeout=self.poll_timeout,
            )

            receipt_or_none = check_transaction_threw(self.client, transaction_hash)
            if receipt_or_none:
                log.critical(
                    'deposit failed',
                    node=pex(self.node_address),
                    contract=pex(self.address),
                )

                self._check_exists()
                raise TransactionThrew('Deposit', receipt_or_none)

            log.info(
                'deposit successful',
                node=pex(self.node_address),
                contract=pex(self.address),
                amount=amount,
            )
Beispiel #13
0
def deploy_service_registry_and_set_urls(
        private_keys, web3, contract_manager,
        service_registry_address) -> Tuple[ServiceRegistry, List[str]]:
    urls = ["http://foo", "http://boo", "http://coo"]
    c1_client = JSONRPCClient(web3, private_keys[0])
    c1_service_proxy = ServiceRegistry(
        jsonrpc_client=c1_client,
        service_registry_address=service_registry_address,
        contract_manager=contract_manager,
    )
    token_address = c1_service_proxy.token_address(block_identifier="latest")
    c1_token_proxy = Token(jsonrpc_client=c1_client,
                           token_address=token_address,
                           contract_manager=contract_manager)
    c2_client = JSONRPCClient(web3, private_keys[1])
    c2_service_proxy = ServiceRegistry(
        jsonrpc_client=c2_client,
        service_registry_address=service_registry_address,
        contract_manager=contract_manager,
    )
    c2_token_proxy = Token(jsonrpc_client=c2_client,
                           token_address=token_address,
                           contract_manager=contract_manager)
    c3_client = JSONRPCClient(web3, private_keys[2])
    c3_service_proxy = ServiceRegistry(
        jsonrpc_client=c3_client,
        service_registry_address=service_registry_address,
        contract_manager=contract_manager,
    )
    c3_token_proxy = Token(jsonrpc_client=c3_client,
                           token_address=token_address,
                           contract_manager=contract_manager)

    # Test that getting a random service for an empty registry returns None
    pfs_address = get_random_pfs(c1_service_proxy,
                                 "latest",
                                 pathfinding_max_fee=TokenAmount(1))
    assert pfs_address is None

    # Test that setting the urls works
    c1_price = c1_service_proxy.current_price(block_identifier="latest")
    tx = c1_token_proxy.proxy.transact("mint", 1000000, c1_price)
    receipt = c1_client.poll(tx)
    assert not check_transaction_threw(receipt=receipt)
    assert c1_token_proxy.balance_of(c1_client.address) > 0
    c1_token_proxy.approve(allowed_address=service_registry_address,
                           allowance=c1_price)
    c1_service_proxy.deposit(block_identifier="latest", limit_amount=c1_price)
    c1_service_proxy.set_url(urls[0])

    c2_price = c2_service_proxy.current_price(block_identifier="latest")
    tx = c2_token_proxy.proxy.transact("mint", 1000000, c2_price)
    receipt = c2_client.poll(tx)
    assert not check_transaction_threw(receipt=receipt)
    assert c2_token_proxy.balance_of(c2_client.address) > 0
    c2_token_proxy.approve(allowed_address=service_registry_address,
                           allowance=c2_price)
    c2_service_proxy.deposit(block_identifier="latest", limit_amount=c2_price)
    c2_service_proxy.set_url(urls[1])

    c3_price = c3_service_proxy.current_price(block_identifier="latest")
    tx = c3_token_proxy.proxy.transact("mint", 1000000, c3_price)
    receipt = c3_client.poll(tx)
    assert not check_transaction_threw(receipt=receipt)
    assert c3_token_proxy.balance_of(c3_client.address) > 0
    c3_token_proxy.approve(allowed_address=service_registry_address,
                           allowance=c3_price)
    c3_service_proxy.deposit(block_identifier="latest", limit_amount=c3_price)
    c3_token_proxy.proxy.transact("mint", 1000000, c3_price)
    c3_token_proxy.approve(allowed_address=service_registry_address,
                           allowance=c3_price)
    c3_service_proxy.set_url(urls[2])

    return c1_service_proxy, urls