예제 #1
0
    def _withdraw_preconditions(
        self, amount_to_withdraw: TokenAmount, given_block_identifier: BlockIdentifier
    ) -> None:
        try:
            withdraw_plan = self.get_withdraw_plan(
                withdrawer_address=self.node_address, block_identifier=given_block_identifier
            )
            whole_balance = self.whole_balance(block_identifier=given_block_identifier)
        except ValueError:
            # If 'given_block_identifier' has been pruned, we can't perform the check
            return
        except BadFunctionCallOutput:
            raise_on_call_returned_empty(given_block_identifier)

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

        given_block_number = self.client.get_block(given_block_identifier)["number"]

        if withdraw_plan.withdraw_block > given_block_number:
            raise BrokenPreconditionError(
                f"Can't withdraw at block {given_block_number}. "
                f"The current withdraw plan requires block number {withdraw_plan.withdraw_block}."
            )

        if whole_balance - amount_to_withdraw < 0:
            raise BrokenPreconditionError(
                f"The current whole balance is {whole_balance}. "
                f"The withdraw of {amount_to_withdraw} would lead to an underflow."
            )
예제 #2
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
예제 #3
0
    def init(
        self,
        monitoring_service_address: MonitoringServiceAddress,
        one_to_n_address: OneToNAddress,
        given_block_identifier: BlockSpecification,
    ) -> None:
        """ Initialize the UserDeposit contract with MS and OneToN addresses """
        log_details = {
            "monitoring_service_address":
            to_checksum_address(monitoring_service_address),
            "one_to_n_address":
            to_checksum_address(one_to_n_address),
        }

        check_address_has_code(
            client=self.client,
            address=Address(monitoring_service_address),
            contract_name=CONTRACT_MONITORING_SERVICE,
            expected_code=decode_hex(
                self.contract_manager.get_runtime_hexcode(
                    CONTRACT_MONITORING_SERVICE)),
        )
        check_address_has_code(
            client=self.client,
            address=Address(one_to_n_address),
            contract_name=CONTRACT_ONE_TO_N,
            expected_code=decode_hex(
                self.contract_manager.get_runtime_hexcode(CONTRACT_ONE_TO_N)),
        )
        try:
            existing_monitoring_service_address = self.monitoring_service_address(
                block_identifier=given_block_identifier)
            existing_one_to_n_address = self.one_to_n_address(
                block_identifier=given_block_identifier)
        except ValueError:
            pass
        except BadFunctionCallOutput:
            raise_on_call_returned_empty(given_block_identifier)
        else:
            if existing_monitoring_service_address != EMPTY_ADDRESS:
                msg = (
                    f"MonitoringService contract address is already set to "
                    f"{to_checksum_address(existing_monitoring_service_address)}"
                )
                raise BrokenPreconditionError(msg)

            if existing_one_to_n_address != EMPTY_ADDRESS:
                msg = (f"OneToN contract address is already set to "
                       f"{to_checksum_address(existing_one_to_n_address)}")
                raise BrokenPreconditionError(msg)

        with log_transaction(log, "init", log_details):
            self._init(
                monitoring_service_address=monitoring_service_address,
                one_to_n_address=one_to_n_address,
                log_details=log_details,
            )
예제 #4
0
    def _plan_withdraw_preconditions(
        self, amount_to_plan_withdraw: TokenAmount, given_block_identifier: BlockIdentifier
    ) -> None:
        """ Check if a WithdrawPlan for the given amount can be created. """
        if amount_to_plan_withdraw <= 0:
            raise BrokenPreconditionError("Planned withdraw amount must be greater than zero.")

        try:
            current_balance = self.get_total_deposit(
                address=self.node_address, block_identifier=given_block_identifier
            )
        except ValueError:
            # If `given_block_identifier` has been pruned we can't perform the check
            return
        except BadFunctionCallOutput:
            raise_on_call_returned_empty(given_block_identifier)

        if current_balance < amount_to_plan_withdraw:
            raise BrokenPreconditionError(
                f"Can't create WithdrawPlan for amount {amount_to_plan_withdraw}, "
                f"it exceeds the current balance of {current_balance}."
            )
예제 #5
0
    def deposit(
        self,
        beneficiary: Address,
        total_deposit: TokenAmount,
        given_block_identifier: BlockSpecification,
    ) -> None:
        """ Deposit provided amount into the user-deposit contract
        to the beneficiary's account. """

        token_address = self.token_address(given_block_identifier)
        token = self.proxy_manager.token(token_address=token_address)

        log_details = {
            "beneficiary": to_checksum_address(beneficiary),
            "contract": to_checksum_address(self.address),
            "node": to_checksum_address(self.node_address),
            "total_deposit": total_deposit,
        }

        # To prevent concurrent transactions for token transfers where it is unknown if
        # we have enough capacity for both, we acquire the lock
        # for the token proxy. Example: A user deposit and a channel deposit
        # for the same token.
        with self.deposit_lock, token.token_lock:
            # check preconditions
            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:
                log_details["previous_total_deposit"] = previous_total_deposit
                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)

            with log_transaction(log, "deposit", log_details):
                self._deposit(
                    beneficiary=beneficiary,
                    token=token,
                    total_deposit=total_deposit,
                    amount_to_deposit=amount_to_deposit,
                    log_details=log_details,
                )
예제 #6
0
    def add_token(
        self,
        token_address: TokenAddress,
        channel_participant_deposit_limit: TokenAmount,
        token_network_deposit_limit: TokenAmount,
        given_block_identifier: BlockIdentifier,
    ) -> Tuple[TransactionHash, TokenNetworkAddress]:
        """
        Register token of `token_address` with the token network.
        The limits apply for version 0.13.0 and above of raiden-contracts,
        since instantiation also takes the limits as constructor arguments.
        """
        if given_block_identifier == BLOCK_ID_LATEST:
            raise ValueError(
                'Calling a proxy with "latest" is usually wrong because '
                "the result of the precondition check is not precisely predictable."
            )

        if token_address == NULL_ADDRESS_BYTES:
            raise InvalidTokenAddress(
                "The call to register a token at 0x00..00 will fail.")

        if token_network_deposit_limit <= 0:
            raise InvalidTokenNetworkDepositLimit(
                f"Token network deposit limit must be larger than zero, "
                f"{token_network_deposit_limit} given.")

        if channel_participant_deposit_limit <= 0:
            raise InvalidTokenNetworkDepositLimit(
                f"Participant deposit limit must be larger than zero, "
                f"{channel_participant_deposit_limit} given")

        if channel_participant_deposit_limit > token_network_deposit_limit:
            raise InvalidChannelParticipantDepositLimit(
                f"Participant deposit limit must be smaller than the network "
                f"deposit limit, {channel_participant_deposit_limit} is larger "
                f"than {token_network_deposit_limit}.")

        token_proxy = self.proxy_manager.token(token_address,
                                               given_block_identifier)
        try:
            token_supply = token_proxy.total_supply(
                block_identifier=given_block_identifier)
            already_registered = self.get_token_network(
                token_address=token_address,
                block_identifier=given_block_identifier)
            deprecation_executor = self.get_deprecation_executor(
                block_identifier=given_block_identifier)
            settlement_timeout_min = self.settlement_timeout_min(
                block_identifier=given_block_identifier)
            settlement_timeout_max = self.settlement_timeout_max(
                block_identifier=given_block_identifier)
            chain_id = self.get_chain_id(
                block_identifier=given_block_identifier)
            secret_registry_address = self.get_secret_registry_address(
                block_identifier=given_block_identifier)
            max_token_networks = self.get_max_token_networks(
                block_identifier=given_block_identifier)
            token_networks_created = self.get_token_network_created(
                block_identifier=given_block_identifier)
        except ValueError:
            # If `given_block_identifier` has been pruned the checks cannot be performed
            pass
        except BadFunctionCallOutput:
            raise_on_call_returned_empty(given_block_identifier)
        else:
            if token_networks_created >= max_token_networks:
                raise MaxTokenNetworkNumberReached(
                    f"Number of token networks will exceed the maximum of {max_token_networks}"
                )

            if token_supply is None:
                raise InvalidToken("Given token address does not follow the "
                                   "ERC20 standard (missing `totalSupply()`)")
            if already_registered:
                raise BrokenPreconditionError(
                    "The token is already registered in the TokenNetworkRegistry."
                )

            if deprecation_executor == NULL_ADDRESS_BYTES:
                raise BrokenPreconditionError(
                    "The deprecation executor property for the TokenNetworkRegistry is invalid."
                )

            if chain_id == 0:
                raise BrokenPreconditionError(
                    "The chain ID property for the TokenNetworkRegistry is invalid."
                )

            if chain_id != self.rpc_client.chain_id:
                raise BrokenPreconditionError(
                    f"The provided chain ID {chain_id} does not match the "
                    f"network Raiden is running on: {self.rpc_client.chain_id}."
                )

            if secret_registry_address == NULL_ADDRESS_BYTES:
                raise BrokenPreconditionError(
                    "The secret registry address for the token network is invalid."
                )

            if settlement_timeout_min == 0:
                raise BrokenPreconditionError(
                    "The minimum settlement timeout for the token network "
                    "should be larger than zero.")

            if settlement_timeout_max <= settlement_timeout_min:
                raise BrokenPreconditionError(
                    "The maximum settlement timeout for the token network "
                    "should be larger than the minimum settlement timeout.")

        log_details = {
            "given_block_identifier": format_block_id(given_block_identifier)
        }
        return self._add_token(
            token_address=token_address,
            channel_participant_deposit_limit=channel_participant_deposit_limit,
            token_network_deposit_limit=token_network_deposit_limit,
            log_details=log_details,
        )
예제 #7
0
    def add_token(
        self,
        token_address: TokenAddress,
        channel_participant_deposit_limit: TokenAmount,
        token_network_deposit_limit: TokenAmount,
        block_identifier: BlockSpecification,
    ) -> TokenNetworkAddress:
        """
        Register token of `token_address` with the token network.
        The limits apply for version 0.13.0 and above of raiden-contracts,
        since instantiation also takes the limits as constructor arguments.
        """
        if block_identifier == "latest":
            raise ValueError(
                'Calling a proxy with "latest" is usually wrong because '
                "the result of the precondition check is not precisely predictable."
            )

        if token_address == NULL_ADDRESS_BYTES:
            raise InvalidTokenAddress(
                "The call to register a token at 0x00..00 will fail.")

        if token_network_deposit_limit <= 0:
            raise InvalidTokenNetworkDepositLimit(
                f"Token network deposit limit of {token_network_deposit_limit} is invalid"
            )

        if channel_participant_deposit_limit > token_network_deposit_limit:
            raise InvalidChannelParticipantDepositLimit(
                f"Channel participant deposit limit of "
                f"{channel_participant_deposit_limit} is invalid")

        token_proxy = self.proxy_manager.token(token_address)
        try:
            token_supply = token_proxy.total_supply(
                block_identifier=block_identifier)
            already_registered = self.get_token_network(
                token_address=token_address, block_identifier=block_identifier)
            deprecation_executor = self.get_deprecation_executor(
                block_identifier=block_identifier)
            settlement_timeout_min = self.settlement_timeout_min(
                block_identifier=block_identifier)
            settlement_timeout_max = self.settlement_timeout_max(
                block_identifier=block_identifier)
            chain_id = self.get_chain_id(block_identifier=block_identifier)
            secret_registry_address = self.get_secret_registry_address(
                block_identifier=block_identifier)
            max_token_networks = self.get_max_token_networks(
                block_identifier=block_identifier)
            token_networks_created = self.get_token_network_created(
                block_identifier=block_identifier)
        except ValueError:
            # If `block_identifier` has been pruned the checks cannot be performed
            pass
        except BadFunctionCallOutput:
            raise_on_call_returned_empty(block_identifier)
        else:
            if token_networks_created >= max_token_networks:
                raise BrokenPreconditionError(
                    f"Number of token networks will exceed the max of {max_token_networks}"
                )

            if token_supply == "":
                raise InvalidToken("Given token address does not follow the "
                                   "ERC20 standard (missing `totalSupply()`)")
            if already_registered:
                raise BrokenPreconditionError(
                    "The token is already registered in the TokenNetworkRegistry."
                )

            if deprecation_executor == NULL_ADDRESS_BYTES:
                raise BrokenPreconditionError(
                    "The deprecation executor property for the TokenNetworkRegistry is invalid."
                )

            if chain_id == 0:
                raise BrokenPreconditionError(
                    "The chain ID property for the TokenNetworkRegistry is invalid."
                )

            if secret_registry_address == NULL_ADDRESS_BYTES:
                raise BrokenPreconditionError(
                    "The secret registry address for the token network is invalid."
                )

            if settlement_timeout_min == 0:
                raise BrokenPreconditionError(
                    "The minimum settlement timeout for the token network "
                    "should be larger than zero.")

            if settlement_timeout_min == 0:
                raise BrokenPreconditionError(
                    "The minimum settlement timeout for the token network "
                    "should be larger than zero.")

            if settlement_timeout_max <= settlement_timeout_min:
                raise BrokenPreconditionError(
                    "The maximum settlement timeout for the token network "
                    "should be larger than the minimum settlement timeout.")

        log_details = {
            "node": to_checksum_address(self.node_address),
            "contract": to_checksum_address(self.address),
            "token_address": to_checksum_address(token_address),
        }
        with log_transaction(log, "add_token", log_details):
            return self._add_token(
                token_address=token_address,
                channel_participant_deposit_limit=
                channel_participant_deposit_limit,
                token_network_deposit_limit=token_network_deposit_limit,
                log_details=log_details,
            )