Пример #1
0
    def register_secret_batch(
        self,
        secrets: List[Secret],
        given_block_identifier: BlockSpecification,
    ):
        secrets_to_register = list()
        secrethashes_to_register = list()
        secrethashes_not_sent = list()
        secret_registry_transaction = AsyncResult()

        for secret in secrets:
            secrethash = sha3(secret)
            secrethash_hex = encode_hex(secrethash)

            is_register_needed = (
                not self.check_registered(secrethash, given_block_identifier)
                and secret not in self.open_secret_transactions)
            if is_register_needed:
                secrets_to_register.append(secret)
                secrethashes_to_register.append(secrethash_hex)
                self.open_secret_transactions[
                    secret] = secret_registry_transaction
            else:
                secrethashes_not_sent.append(secrethash_hex)

        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'secrethashes': secrethashes_to_register,
            'secrethashes_not_sent': secrethashes_not_sent,
        }

        if not secrets_to_register:
            log.debug('registerSecretBatch skipped', **log_details)
            return

        checking_block = self.client.get_checking_block()
        error_prefix = 'Call to registerSecretBatch will fail'
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            'registerSecretBatch', secrets)
        if gas_limit:
            error_prefix = 'Call to registerSecretBatch failed'
            try:
                gas_limit = safe_gas_limit(
                    gas_limit,
                    len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH,
                )
                transaction_hash = self.proxy.transact('registerSecretBatch',
                                                       gas_limit, secrets)
                self.client.poll(transaction_hash)
                receipt_or_none = check_transaction_threw(
                    self.client, transaction_hash)
            except Exception as e:
                secret_registry_transaction.set_exception(e)
                msg = 'Unexpected exception at sending registerSecretBatch transaction'
            else:
                secret_registry_transaction.set(transaction_hash)
            finally:
                for secret in secrets_to_register:
                    self.open_secret_transactions.pop(secret, None)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = checking_block

            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='registerSecretBatch',
                transaction_executed=transaction_executed,
                required_gas=len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH,
                block_identifier=block,
            )
            error_msg = f'{error_prefix}. {msg}'
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        log.info('registerSecretBatch successful', **log_details)
Пример #2
0
    def handle_contract_send_channelunlock(
        raiden: "RaidenService",
        chain_state: ChainState,
        channel_unlock_event: ContractSendChannelBatchUnlock,
    ):
        assert raiden.wal, "The Raiden Service must be initialize to handle events"

        canonical_identifier = channel_unlock_event.canonical_identifier
        token_network_identifier = canonical_identifier.token_network_address
        channel_identifier = canonical_identifier.channel_identifier
        participant = channel_unlock_event.participant

        payment_channel: PaymentChannel = raiden.chain.payment_channel(
            canonical_identifier=canonical_identifier)

        channel_state = get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_id=TokenNetworkID(token_network_identifier),
            creator_address=raiden.address,
            partner_address=participant,
        )

        if not channel_state:
            # channel was cleaned up already due to an unlock
            raise RaidenUnrecoverableError(
                f"Failed to find channel state with partner:"
                f"{to_checksum_address(participant)}, token_network:pex(token_network_identifier)"
            )

        our_address = channel_state.our_state.address
        our_locksroot = channel_state.our_state.onchain_locksroot

        partner_address = channel_state.partner_state.address
        partner_locksroot = channel_state.partner_state.onchain_locksroot

        # we want to unlock because there are on-chain unlocked locks
        search_events = our_locksroot != EMPTY_HASH
        # we want to unlock, because there are unlocked/unclaimed locks
        search_state_changes = partner_locksroot != EMPTY_HASH

        if not search_events and not search_state_changes:
            # In the case that someone else sent the unlock we do nothing
            # Check https://github.com/raiden-network/raiden/issues/3152
            # for more details
            log.warning(
                "Onchain unlock already mined",
                canonical_identifier=canonical_identifier,
                channel_identifier=canonical_identifier.channel_identifier,
                participant=to_checksum_address(participant),
            )
            return

        if search_state_changes:
            state_change_record = get_state_change_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                locksroot=partner_locksroot,
                sender=partner_address,
            )
            state_change_identifier = state_change_record.state_change_identifier

            if not state_change_identifier:
                raise RaidenUnrecoverableError(
                    f"Failed to find state that matches the current channel locksroots. "
                    f"chain_id:{raiden.chain.network_id} "
                    f"token_network:{to_checksum_address(token_network_identifier)} "
                    f"channel:{channel_identifier} "
                    f"participant:{to_checksum_address(participant)} "
                    f"our_locksroot:{to_hex(our_locksroot)} "
                    f"partner_locksroot:{to_hex(partner_locksroot)} ")

            restored_channel_state = channel_state_until_state_change(
                raiden=raiden,
                canonical_identifier=canonical_identifier,
                state_change_identifier=state_change_identifier,
            )
            assert restored_channel_state is not None

            gain = get_batch_unlock_gain(restored_channel_state)

            skip_unlock = (restored_channel_state.partner_state.address
                           == participant and gain.from_partner_locks == 0)
            if not skip_unlock:
                unlock(
                    raiden=raiden,
                    payment_channel=payment_channel,
                    end_state=restored_channel_state.partner_state,
                    participant=our_address,
                    partner=partner_address,
                )

        if search_events:
            event_record = get_event_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                locksroot=our_locksroot,
                recipient=partner_address,
            )
            state_change_identifier = event_record.state_change_identifier

            if not state_change_identifier:
                raise RaidenUnrecoverableError(
                    f"Failed to find event that match current channel locksroots. "
                    f"chain_id:{raiden.chain.network_id} "
                    f"token_network:{to_checksum_address(token_network_identifier)} "
                    f"channel:{channel_identifier} "
                    f"participant:{to_checksum_address(participant)} "
                    f"our_locksroot:{to_hex(our_locksroot)} "
                    f"partner_locksroot:{to_hex(partner_locksroot)} ")

            restored_channel_state = channel_state_until_state_change(
                raiden=raiden,
                canonical_identifier=canonical_identifier,
                state_change_identifier=state_change_identifier,
            )
            assert restored_channel_state is not None

            gain = get_batch_unlock_gain(restored_channel_state)

            skip_unlock = (restored_channel_state.our_state.address
                           == participant and gain.from_our_locks == 0)
            if not skip_unlock:
                unlock(
                    raiden=raiden,
                    payment_channel=payment_channel,
                    end_state=restored_channel_state.our_state,
                    participant=partner_address,
                    partner=our_address,
                )
Пример #3
0
    def handle_contract_send_channelsettle(
            raiden: "RaidenService",
            channel_settle_event: ContractSendChannelSettle):
        # only make settlements if our node is a participant, dont do it for lc
        if channel_settle_event.channel_state.our_state.address == raiden.address or channel_settle_event.channel_state.partner_state.address == raiden.address:
            assert raiden.wal, "The Raiden Service must be initialize to handle events"
            canonical_identifier = CanonicalIdentifier(
                chain_identifier=raiden.chain.network_id,
                token_network_address=channel_settle_event.
                token_network_identifier,
                channel_identifier=channel_settle_event.channel_identifier,
            )
            triggered_by_block_hash = channel_settle_event.triggered_by_block_hash
            payment_channel: PaymentChannel = raiden.chain.payment_channel(
                canonical_identifier=canonical_identifier)
            token_network_proxy: TokenNetwork = payment_channel.token_network
            if not token_network_proxy.client.can_query_state_for_block(
                    triggered_by_block_hash):
                # The only time this can happen is during restarts after a long time
                # when the triggered block ends up getting pruned
                # In that case it's safe to just use the latest view of the chain to
                # query the on-chain participant/channel details
                triggered_by_block_hash = token_network_proxy.client.blockhash_from_blocknumber(
                    "latest")
            participants_details = token_network_proxy.detail_participants(
                participant1=payment_channel.participant1,
                participant2=payment_channel.participant2,
                block_identifier=triggered_by_block_hash,
                channel_identifier=channel_settle_event.channel_identifier,
            )
            our_details = participants_details.our_details
            partner_details = participants_details.partner_details
            log_details = {
                "chain_id": canonical_identifier.chain_identifier,
                "token_network_identifier":
                canonical_identifier.token_network_address,
                "channel_identifier": canonical_identifier.channel_identifier,
                "node": pex(raiden.address),
                "partner": to_checksum_address(partner_details.address),
                "our_deposit": our_details.deposit,
                "our_withdrawn": our_details.withdrawn,
                "our_is_closer": our_details.is_closer,
                "our_balance_hash": to_hex(our_details.balance_hash),
                "our_nonce": our_details.nonce,
                "our_locksroot": to_hex(our_details.locksroot),
                "our_locked_amount": our_details.locked_amount,
                "partner_deposit": partner_details.deposit,
                "partner_withdrawn": partner_details.withdrawn,
                "partner_is_closer": partner_details.is_closer,
                "partner_balance_hash": to_hex(partner_details.balance_hash),
                "partner_nonce": partner_details.nonce,
                "partner_locksroot": to_hex(partner_details.locksroot),
                "partner_locked_amount": partner_details.locked_amount,
            }
            if our_details.balance_hash != EMPTY_HASH:
                event_record = get_event_with_balance_proof_by_balance_hash(
                    storage=raiden.wal.storage,
                    canonical_identifier=canonical_identifier,
                    balance_hash=our_details.balance_hash,
                )
                if event_record.data is None:
                    log.critical("our balance proof not found", **log_details)
                    raise RaidenUnrecoverableError(
                        "Our balance proof could not be found in the database")
                our_balance_proof = event_record.data.balance_proof
                our_transferred_amount = our_balance_proof.transferred_amount
                our_locked_amount = our_balance_proof.locked_amount
                our_locksroot = our_balance_proof.locksroot
            else:
                our_transferred_amount = 0
                our_locked_amount = 0
                our_locksroot = EMPTY_HASH
            if partner_details.balance_hash != EMPTY_HASH:
                state_change_record = get_state_change_with_balance_proof_by_balance_hash(
                    storage=raiden.wal.storage,
                    canonical_identifier=canonical_identifier,
                    balance_hash=partner_details.balance_hash,
                    sender=participants_details.partner_details.address,
                )
                if state_change_record.data is None:
                    log.critical("partner balance proof not found",
                                 **log_details)
                    raise RaidenUnrecoverableError(
                        "Partner balance proof could not be found in the database"
                    )
                partner_balance_proof = state_change_record.data.balance_proof
                partner_transferred_amount = partner_balance_proof.transferred_amount
                partner_locked_amount = partner_balance_proof.locked_amount
                partner_locksroot = partner_balance_proof.locksroot
            else:
                partner_transferred_amount = 0
                partner_locked_amount = 0
                partner_locksroot = EMPTY_HASH

            payment_channel.settle(
                transferred_amount=our_transferred_amount,
                locked_amount=our_locked_amount,
                locksroot=our_locksroot,
                partner_transferred_amount=partner_transferred_amount,
                partner_locked_amount=partner_locked_amount,
                partner_locksroot=partner_locksroot,
                block_identifier=triggered_by_block_hash,
            )
        else:
            log.info("Ignoring settlement cause is a light client")
Пример #4
0
    def handle_contract_send_channelunlock(
        self,
        raiden: RaidenService,
        channel_unlock_event: ContractSendChannelBatchUnlock,
    ):
        payment_channel: PaymentChannel = raiden.chain.payment_channel(
            channel_unlock_event.token_network_identifier,
            channel_unlock_event.channel_identifier,
        )
        token_network: TokenNetwork = payment_channel.token_network

        # Fetch on-chain balance hashes for both participants
        participants_details = token_network.detail_participants(
            raiden.address,
            channel_unlock_event.participant,
            channel_unlock_event.channel_identifier,
        )

        our_details = participants_details.our_details
        our_locksroot = our_details.locksroot

        partner_details = participants_details.partner_details
        partner_locksroot = partner_details.locksroot

        if (partner_details.address == channel_unlock_event.participant
                and partner_locksroot != EMPTY_HASH):
            # Partner account
            record = raiden.wal.storage.get_latest_state_change_by_data_field({
                'balance_proof.chain_id':
                raiden.chain.network_id,
                'balance_proof.token_network_identifier':
                to_checksum_address(
                    channel_unlock_event.token_network_identifier, ),
                'balance_proof.channel_identifier':
                channel_unlock_event.channel_identifier,
                'balance_proof.sender':
                to_checksum_address(
                    participants_details.partner_details.address, ),
                'balance_proof.locksroot':
                serialize_bytes(partner_locksroot),
            })
        elif (our_details.address == channel_unlock_event.participant
              and our_locksroot != EMPTY_HASH):
            # Our account
            record = raiden.wal.storage.get_latest_event_by_data_field({
                'balance_proof.chain_id':
                raiden.chain.network_id,
                'balance_proof.token_network_identifier':
                to_checksum_address(
                    channel_unlock_event.token_network_identifier, ),
                'balance_proof.locksroot':
                serialize_bytes(our_locksroot),
                'channel_identifier':
                channel_unlock_event.channel_identifier,
            })
        else:
            raise RaidenUnrecoverableError(
                "Failed to find state/event that match current channel locksroots",
            )

        # Replay state changes until a channel state is reached where
        # this channel state has the participants balance hash.
        restored_channel_state = channel_state_until_state_change(
            raiden=raiden,
            payment_network_identifier=raiden.default_registry.address,
            token_address=channel_unlock_event.token_address,
            channel_identifier=channel_unlock_event.channel_identifier,
            state_change_identifier=record.state_change_identifier,
        )

        # Compute merkle tree leaves from partner state
        our_state = restored_channel_state.our_state
        partner_state = restored_channel_state.partner_state
        if partner_state.address == channel_unlock_event.participant:  # Partner account
            merkle_tree_leaves = get_batch_unlock(partner_state)
        elif our_state.address == channel_unlock_event.participant:  # Our account
            merkle_tree_leaves = get_batch_unlock(our_state)

        try:
            payment_channel.unlock(merkle_tree_leaves)
        except ChannelOutdatedError as e:
            log.error(str(e))
Пример #5
0
    def handle_contract_send_channelsettle(
        raiden: "RaidenService", channel_settle_event: ContractSendChannelSettle
    ) -> None:
        assert raiden.wal, "The Raiden Service must be initialize to handle events"

        canonical_identifier = CanonicalIdentifier(
            chain_identifier=raiden.rpc_client.chain_id,
            token_network_address=channel_settle_event.token_network_address,
            channel_identifier=channel_settle_event.channel_identifier,
        )
        triggered_by_block_hash = channel_settle_event.triggered_by_block_hash

        chain_state = state_from_raiden(raiden)
        channel_state = get_channelstate_by_canonical_identifier(
            chain_state=chain_state, canonical_identifier=canonical_identifier
        )

        if channel_state is None:
            raise RaidenUnrecoverableError("ContractSendChannelSettle for non-existing channel.")

        confirmed_block_identifier = chain_state.block_hash
        payment_channel: PaymentChannel = raiden.proxy_manager.payment_channel(
            channel_state=channel_state, block_identifier=confirmed_block_identifier
        )
        token_network_proxy: TokenNetwork = payment_channel.token_network

        try:
            participants_details = token_network_proxy.detail_participants(
                participant1=payment_channel.participant1,
                participant2=payment_channel.participant2,
                block_identifier=triggered_by_block_hash,
                channel_identifier=channel_settle_event.channel_identifier,
            )
        except ValueError:
            # The triggered_by_block_hash block was pruned.
            participants_details = token_network_proxy.detail_participants(
                participant1=payment_channel.participant1,
                participant2=payment_channel.participant2,
                block_identifier=BLOCK_ID_LATEST,
                channel_identifier=channel_settle_event.channel_identifier,
            )

        our_details = participants_details.our_details
        partner_details = participants_details.partner_details

        log_details = {
            "chain_id": canonical_identifier.chain_identifier,
            "token_network_address": canonical_identifier.token_network_address,
            "channel_identifier": canonical_identifier.channel_identifier,
            "node": to_checksum_address(raiden.address),
            "partner": to_checksum_address(partner_details.address),
            "our_deposit": our_details.deposit,
            "our_withdrawn": our_details.withdrawn,
            "our_is_closer": our_details.is_closer,
            "our_balance_hash": to_hex(our_details.balance_hash),
            "our_nonce": our_details.nonce,
            "our_locksroot": to_hex(our_details.locksroot),
            "our_locked_amount": our_details.locked_amount,
            "partner_deposit": partner_details.deposit,
            "partner_withdrawn": partner_details.withdrawn,
            "partner_is_closer": partner_details.is_closer,
            "partner_balance_hash": to_hex(partner_details.balance_hash),
            "partner_nonce": partner_details.nonce,
            "partner_locksroot": to_hex(partner_details.locksroot),
            "partner_locked_amount": partner_details.locked_amount,
        }

        if our_details.balance_hash != EMPTY_BALANCE_HASH:
            event_record = get_event_with_balance_proof_by_balance_hash(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                balance_hash=our_details.balance_hash,
                recipient=participants_details.partner_details.address,
            )

            if event_record is None:
                log.critical("our balance proof not found", **log_details)
                raise RaidenUnrecoverableError(
                    "Our balance proof could not be found in the database"
                )

            our_balance_proof = event_record.data.balance_proof  # type: ignore
            our_transferred_amount = our_balance_proof.transferred_amount
            our_locked_amount = our_balance_proof.locked_amount
            our_locksroot = our_balance_proof.locksroot
        else:
            our_transferred_amount = 0
            our_locked_amount = 0
            our_locksroot = LOCKSROOT_OF_NO_LOCKS

        if partner_details.balance_hash != EMPTY_BALANCE_HASH:
            state_change_record = get_state_change_with_balance_proof_by_balance_hash(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                balance_hash=partner_details.balance_hash,
                sender=participants_details.partner_details.address,
            )
            if state_change_record is None:
                log.critical("partner balance proof not found", **log_details)
                raise RaidenUnrecoverableError(
                    "Partner balance proof could not be found in the database"
                )

            partner_balance_proof = state_change_record.data.balance_proof  # type: ignore
            partner_transferred_amount = partner_balance_proof.transferred_amount
            partner_locked_amount = partner_balance_proof.locked_amount
            partner_locksroot = partner_balance_proof.locksroot
        else:
            partner_transferred_amount = 0
            partner_locked_amount = 0
            partner_locksroot = LOCKSROOT_OF_NO_LOCKS

        try:
            payment_channel.settle(
                transferred_amount=our_transferred_amount,
                locked_amount=our_locked_amount,
                locksroot=our_locksroot,
                partner_transferred_amount=partner_transferred_amount,
                partner_locked_amount=partner_locked_amount,
                partner_locksroot=partner_locksroot,
                block_identifier=triggered_by_block_hash,
            )
        except InsufficientEth as e:
            raise RaidenUnrecoverableError(str(e)) from e
Пример #6
0
    def update_transfer(
        self,
        channel_identifier: typing.ChannelID,
        partner: typing.Address,
        balance_hash: typing.BalanceHash,
        nonce: typing.Nonce,
        additional_hash: typing.AdditionalHash,
        closing_signature: typing.Signature,
        non_closing_signature: typing.Signature,
    ):
        log_details = {
            'token_network': pex(self.address),
            'node': pex(self.node_address),
            'partner': pex(partner),
            'nonce': nonce,
            'balance_hash': encode_hex(balance_hash),
            'additional_hash': encode_hex(additional_hash),
            'closing_signature': encode_hex(closing_signature),
            'non_closing_signature': encode_hex(non_closing_signature),
        }
        log.debug('updateNonClosingBalanceProof called', **log_details)

        data_that_was_signed = pack_balance_proof(
            nonce=nonce,
            balance_hash=balance_hash,
            additional_hash=additional_hash,
            channel_identifier=channel_identifier,
            token_network_identifier=typing.TokenNetworkID(self.address),
            chain_id=self.proxy.contract.functions.chain_id().call(),
        )

        try:
            signer_address = eth_recover(
                data=data_that_was_signed,
                signature=closing_signature,
            )

            # InvalidSignature is raised by eth_utils.eth_recover if signature
            # is not bytes or has the incorrect length
            #
            # ValueError is raised if the PublicKey instantiation failed, let it
            # propagate because it's a memory pressure problem.
            #
            # Exception is raised if the public key recovery failed.
        except Exception:  # pylint: disable=broad-except
            msg = "Couldn't verify the balance proof signature"
            log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                         **log_details)
            raise RaidenUnrecoverableError(msg)

        if signer_address != partner:
            msg = 'Invalid balance proof signature'
            log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                         **log_details)
            raise RaidenUnrecoverableError(msg)

        self._check_for_outdated_channel(
            self.node_address,
            partner,
            channel_identifier,
        )

        detail = self.detail_channel(
            participant1=self.node_address,
            participant2=partner,
            channel_identifier=channel_identifier,
        )
        if detail.state != ChannelState.CLOSED:
            msg = 'Channel is not in a closed state'
            log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                         **log_details)
            raise RaidenUnrecoverableError(msg)

        if detail.settle_block_number < self.client.block_number():
            msg = ('updateNonClosingBalanceProof cannot be called '
                   'because the settlement period is over')
            log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                         **log_details)
            raise RaidenRecoverableError(msg)

        transaction_hash = self.proxy.transact(
            'updateNonClosingBalanceProof',
            safe_gas_limit(GAS_REQUIRED_FOR_UPDATE_BALANCE_PROOF),
            channel_identifier,
            partner,
            self.node_address,
            balance_hash,
            nonce,
            additional_hash,
            closing_signature,
            non_closing_signature,
        )

        self.client.poll(transaction_hash)

        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)
        if receipt_or_none:
            if detail.settle_block_number < receipt_or_none['blockNumber']:
                msg = ('updateNonClosingBalanceProof transaction '
                       'was mined after settlement finished')
                log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                             **log_details)
                raise RaidenRecoverableError(msg)

            # This should never happen if the settlement window and gas price
            # estimation is done properly
            channel_settled = self.channel_is_settled(
                participant1=self.node_address,
                participant2=partner,
                channel_identifier=channel_identifier,
            )
            if channel_settled is True:
                msg = 'Channel is settled'
                log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                             **log_details)
                raise RaidenRecoverableError(msg)

            msg = 'Update NonClosing balance proof'
            log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                         **log_details)
            raise TransactionThrew(msg, receipt_or_none)

        log.info('updateNonClosingBalanceProof successful', **log_details)
Пример #7
0
    def handle_contract_send_channelunlock(
        self,
        raiden: RaidenService,
        channel_unlock_event: ContractSendChannelBatchUnlock,
    ):
        token_network_identifier = channel_unlock_event.token_network_identifier
        channel_identifier = channel_unlock_event.channel_identifier
        participant = channel_unlock_event.participant
        token_address = channel_unlock_event.token_address

        payment_channel: PaymentChannel = raiden.chain.payment_channel(
            token_network_address=token_network_identifier,
            channel_id=channel_identifier,
        )
        token_network: TokenNetwork = payment_channel.token_network

        participants_details = token_network.detail_participants(
            participant1=raiden.address,
            participant2=participant,
            block_identifier='latest',
            channel_identifier=channel_identifier,
        )

        our_details = participants_details.our_details
        our_locksroot = our_details.locksroot

        partner_details = participants_details.partner_details
        partner_locksroot = partner_details.locksroot

        is_partner_unlock = (partner_details.address == participant
                             and partner_locksroot != EMPTY_HASH)
        is_our_unlock = (our_details.address == participant
                         and our_locksroot != EMPTY_HASH)

        if is_partner_unlock:
            state_change_record = get_state_change_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                chain_id=raiden.chain.network_id,
                token_network_identifier=token_network_identifier,
                channel_identifier=channel_identifier,
                locksroot=partner_locksroot,
                sender=participants_details.partner_details.address,
            )
            state_change_identifier = state_change_record.state_change_identifier
        elif is_our_unlock:
            event_record = get_event_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                chain_id=raiden.chain.network_id,
                token_network_identifier=token_network_identifier,
                channel_identifier=channel_identifier,
                locksroot=our_locksroot.balance_hash,
            )
            state_change_identifier = event_record.state_change_identifier
        else:
            # In the case that someone else sent the unlock we do nothing
            # Check https://github.com/raiden-network/raiden/issues/3152
            # for more details
            log.warning(
                'Onchain unlock already mined',
                token_address=token_address,
                channel_identifier=channel_identifier,
                participant=participant,
            )
            return

        if not state_change_identifier:
            raise RaidenUnrecoverableError(
                f'Failed to find state/event that match current channel locksroots. '
                f'chain_id:{raiden.chain.network_id} '
                f'token:{to_checksum_address(token_address)} '
                f'token_network:{to_checksum_address(token_network_identifier)} '
                f'channel:{channel_identifier} '
                f'participant:{to_checksum_address(participant)} '
                f'our_locksroot:{to_hex(our_locksroot)} '
                f'our_balance_hash:{to_hex(our_details.balance_hash)} '
                f'partner_locksroot:{to_hex(partner_locksroot)} '
                f'partner_balancehash:{to_hex(partner_details.balance_hash)} ',
            )

        # Replay state changes until a channel state is reached where
        # this channel state has the participants balance hash.
        restored_channel_state = channel_state_until_state_change(
            raiden=raiden,
            payment_network_identifier=raiden.default_registry.address,
            token_address=token_address,
            channel_identifier=channel_identifier,
            state_change_identifier=state_change_identifier,
        )

        our_state = restored_channel_state.our_state
        partner_state = restored_channel_state.partner_state
        if partner_state.address == participant:
            merkle_tree_leaves = get_batch_unlock(partner_state)
        elif our_state.address == participant:
            merkle_tree_leaves = get_batch_unlock(our_state)

        try:
            # LEFTODO: Supply a proper block id
            payment_channel.unlock(
                merkle_tree_leaves=merkle_tree_leaves,
                block_identifier='latest',
            )
        except ChannelOutdatedError as e:
            log.error(
                str(e),
                node=pex(raiden.address),
            )
Пример #8
0
    def _register_secret_batch(
        self,
        secrets_to_register: List[Secret],
        transaction_result: AsyncResult,
        log_details: Dict[Any, Any],
    ) -> None:
        checking_block = self.client.get_checking_block()
        gas_limit = self.proxy.estimate_gas(
            checking_block, "registerSecretBatch", secrets_to_register
        )
        receipt = None
        transaction_hash = None
        msg = None

        if gas_limit:
            gas_limit = safe_gas_limit(
                gas_limit, len(secrets_to_register) * GAS_REQUIRED_PER_SECRET_IN_BATCH
            )
            log_details["gas_limit"] = gas_limit

            try:
                transaction_hash = self.proxy.transact(
                    "registerSecretBatch", gas_limit, secrets_to_register
                )
                receipt = self.client.poll(transaction_hash)
            except Exception as e:  # pylint: disable=broad-except
                msg = f"Unexpected exception {e} at sending registerSecretBatch transaction."

        # Clear `open_secret_transactions` regardless of the transaction being
        # successfully executed or not.
        with self._open_secret_transactions_lock:
            for secret in secrets_to_register:
                self.open_secret_transactions.pop(secret)

        # As of version `0.4.0` of the contract has *no* asserts or requires.
        # Therefore the only reason for the transaction to fail is if there is
        # a bug.
        unrecoverable_error = (
            gas_limit is None or receipt is None or receipt["status"] == RECEIPT_FAILURE_CODE
        )

        exception: Union[RaidenRecoverableError, RaidenUnrecoverableError]
        if unrecoverable_error:
            # If the transaction was sent it must not fail. If this happened
            # some of our assumptions is broken therefore the error is
            # unrecoverable
            if receipt is not None:
                if receipt["gasUsed"] == gas_limit:
                    # The transaction failed and all gas was used. This can
                    # happen because of:
                    #
                    # - A compiler bug if an invalid opcode was executed.
                    # - A configuration bug if an assert was executed,
                    # because version 0.4.0 of the secret registry does not have an
                    # assert.
                    # - An ethereum client bug if the gas_limit was
                    # underestimated.
                    #
                    # Safety cannot be guaranteed under any of these cases,
                    # this error is unrecoverable.
                    error = (
                        "Secret registration failed because of a bug in either "
                        "the solidity compiler, the running ethereum client, or "
                        "a configuration error in Raiden."
                    )
                else:
                    # The transaction failed and *not* all gas was used. This
                    # can happen because of:
                    #
                    # - A compiler bug if a revert was introduced.
                    # - A configuration bug, because for 0.4.0 the secret
                    # registry does not have a revert.
                    error = (
                        "Secret registration failed because of a configuration "
                        "bug or compiler bug. Please double check the secret "
                        "smart contract is at version 0.4.0, if it is then a "
                        "compiler bug was hit."
                    )

                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # If gas_limit is set and there is no receipt then an exception was
            # raised while sending the transaction. This should only happen if
            # the account is being used concurrently, which is not supported.
            # This can happen because:
            #
            # - The nonce of the transaction was already used
            # - The nonce was reused *and* the account didn't have enough ether
            # to pay for the gas
            #
            # Safety cannot be guaranteed under any of these cases, this error
            # is unrecoverable. *Note*: This assumes the ethereum client
            # takes into account the current transactions in the pool.
            if gas_limit:
                assert msg, "Unexpected control flow, an exception should have been raised."
                error = (
                    f"Sending the the transaction for registerSecretBatch "
                    f"failed with: `{msg}`.  This happens if the same ethereum "
                    f"account is being used by more than one program which is not "
                    f"supported."
                )

                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # gas_limit can fail because:
            #
            # - The Ethereum client detected the transaction could not
            # successfully execute, this happens if an assert/revert is hit.
            # - The account is lacking funds to pay for the gas.
            #
            # Either of these is a bug. The contract does not use
            # assert/revert, and the account should always be funded
            assert gas_limit
            self.proxy.rpc_client.check_for_insufficient_eth(
                transaction_name="registerSecretBatch",
                transaction_executed=True,
                required_gas=gas_limit,
                block_identifier=checking_block,
            )
            error = "Call to registerSecretBatch couldn't be done"

            exception = RaidenRecoverableError(error)
            transaction_result.set_exception(exception)
            raise exception

        # The local **MUST** transaction_result be set before waiting for the
        # other results, otherwise we have a dead-lock
        transaction_result.set(transaction_hash)
Пример #9
0
    def start_mediated_transfer_with_secret(
            self,
            token_network_identifier: TokenNetworkID,
            amount: TokenAmount,
            target: TargetAddress,
            identifier: PaymentID,
            secret: Secret,
    ) -> PaymentStatus:

        secret_hash = sha3(secret)
        # LEFTODO: Supply a proper block id
        secret_registered = self.default_secret_registry.check_registered(
            secrethash=secret_hash,
            block_identifier='latest',
        )
        if secret_registered:
            raise RaidenUnrecoverableError(
                f'Attempted to initiate a locked transfer with secrethash {pex(secret_hash)}.'
                f' That secret is already registered onchain.',
            )

        self.start_health_check_for(Address(target))

        if identifier is None:
            identifier = create_default_identifier()

        with self.payment_identifier_lock:
            payment_status = self.targets_to_identifiers_to_statuses[target].get(identifier)
            if payment_status:
                payment_status_matches = payment_status.matches(
                    token_network_identifier,
                    amount,
                )
                if not payment_status_matches:
                    raise PaymentConflict(
                        'Another payment with the same id is in flight',
                    )

                return payment_status

            payment_status = PaymentStatus(
                payment_identifier=identifier,
                amount=amount,
                token_network_identifier=token_network_identifier,
                payment_done=AsyncResult(),
                secret=secret,
                secret_hash=secret_hash,
            )
            self.targets_to_identifiers_to_statuses[target][identifier] = payment_status

        init_initiator_statechange = initiator_init(
            raiden=self,
            transfer_identifier=identifier,
            transfer_amount=amount,
            transfer_secret=secret,
            token_network_identifier=token_network_identifier,
            target_address=target,
        )

        # Dispatch the state change even if there are no routes to create the
        # wal entry.
        self.handle_state_change(init_initiator_statechange)

        return payment_status
Пример #10
0
def get_contractreceivechannelbatchunlock_data_from_event(
    chain_state: ChainState, storage: SerializedSQLiteStorage, event: DecodedEvent
) -> Optional[CanonicalIdentifier]:
    token_network_address = TokenNetworkAddress(event.originating_contract)
    data = event.event_data
    args = data["args"]
    participant1 = args["receiver"]
    participant2 = args["sender"]
    locksroot = args["locksroot"]

    token_network_state = views.get_token_network_by_address(chain_state, token_network_address)
    assert token_network_state is not None

    if participant1 == chain_state.our_address:
        partner = participant2
    elif participant2 == chain_state.our_address:
        partner = participant1
    else:
        return None

    channel_identifiers = token_network_state.partneraddresses_to_channelidentifiers[partner]
    canonical_identifier = None

    for channel_identifier in channel_identifiers:
        if partner == args["sender"]:
            state_change_record = get_state_change_with_balance_proof_by_locksroot(
                storage=storage,
                canonical_identifier=CanonicalIdentifier(
                    chain_identifier=chain_state.chain_id,
                    token_network_address=token_network_address,
                    channel_identifier=channel_identifier,
                ),
                locksroot=locksroot,
                sender=partner,
            )
            if state_change_record is not None:
                canonical_identifier = (
                    state_change_record.data.balance_proof.canonical_identifier  # type: ignore
                )
                break
        elif partner == args["receiver"]:
            event_record = get_event_with_balance_proof_by_locksroot(
                storage=storage,
                canonical_identifier=CanonicalIdentifier(
                    chain_identifier=chain_state.chain_id,
                    token_network_address=token_network_address,
                    channel_identifier=channel_identifier,
                ),
                locksroot=locksroot,
                recipient=partner,
            )
            if event_record is not None:
                canonical_identifier = (
                    event_record.data.balance_proof.canonical_identifier  # type: ignore
                )
                break

    msg = (
        f"Can not resolve channel_id for unlock with locksroot {to_hex(locksroot)} and "
        f"partner {to_checksum_address(partner)}."
    )
    if canonical_identifier is None:
        raise RaidenUnrecoverableError(msg)

    return canonical_identifier
Пример #11
0
    def _add_token(
        self,
        token_address: TokenAddress,
        channel_participant_deposit_limit: TokenAmount,
        token_network_deposit_limit: TokenAmount,
        log_details: Dict[Any, Any],
    ) -> TokenNetworkAddress:
        token_network_address = None

        kwargs = {
            "_token_address": token_address,
            "_channel_participant_deposit_limit":
            channel_participant_deposit_limit,
            "_token_network_deposit_limit": token_network_deposit_limit,
        }
        estimated_transaction = self.rpc_client.estimate_gas(
            self.proxy, "createERC20TokenNetwork", log_details, **kwargs)

        if estimated_transaction is not None:
            estimated_transaction.estimated_gas = safe_gas_limit(
                estimated_transaction.estimated_gas,
                self.gas_measurements[
                    "TokenNetworkRegistry createERC20TokenNetwork"],
            )

            transaction_sent = self.rpc_client.transact(estimated_transaction)
            transaction_mined = self.rpc_client.poll_transaction(
                transaction_sent)
            receipt = transaction_mined.receipt

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

                max_token_networks = self.get_max_token_networks(
                    block_identifier=failed_at_blocknumber)
                token_networks_created = self.get_token_network_created(
                    block_identifier=failed_at_blocknumber)
                already_registered = self.get_token_network(
                    token_address=token_address,
                    block_identifier=failed_at_blocknumber)
                deprecation_executor = self.get_deprecation_executor(
                    block_identifier=failed_at_blocknumber)
                settlement_timeout_min = self.settlement_timeout_min(
                    block_identifier=failed_at_blocknumber)
                settlement_timeout_max = self.settlement_timeout_max(
                    block_identifier=failed_at_blocknumber)
                chain_id = self.get_chain_id(
                    block_identifier=failed_at_blocknumber)
                secret_registry_address = self.get_secret_registry_address(
                    block_identifier=failed_at_blocknumber)

                try:
                    # Creating a new instance to run the constructor checks.
                    token_proxy = Token(
                        jsonrpc_client=self.rpc_client,
                        token_address=token_address,
                        contract_manager=self.proxy_manager.contract_manager,
                        block_identifier=failed_at_blocknumber,
                    )
                except AddressWithoutCode:
                    # This cannot be an unrecoverable error, since the ERC20
                    # code is external.
                    raise RaidenRecoverableError(
                        "Token disappeared! The address "
                        f"{to_checksum_address(token_address)} did have code at "
                        f"block {log_details['given_block_identifier']}, however "
                        f"at block {failed_at_blocknumber} when the registration "
                        "transaction was mined the address didn't have code "
                        "anymore.")

                check_transaction_failure(transaction_mined, self.rpc_client)

                check_address_has_code_handle_pruned_block(
                    client=self.rpc_client,
                    address=Address(secret_registry_address),
                    contract_name=CONTRACT_SECRET_REGISTRY,
                    expected_code=decode_hex(
                        self.proxy_manager.contract_manager.
                        get_runtime_hexcode(CONTRACT_SECRET_REGISTRY)),
                    given_block_identifier=failed_at_blocknumber,
                )

                if token_networks_created >= max_token_networks:
                    raise RaidenRecoverableError(
                        "The number of existing token networks reached the maximum allowed"
                    )

                if already_registered:
                    # Race condition lost, the token network was created in a different
                    # transaction which got mined first.
                    raise RaidenRecoverableError(
                        "The token was already registered in the TokenNetworkRegistry."
                    )

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

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

                if chain_id != self.rpc_client.chain_id:
                    raise RaidenUnrecoverableError(
                        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 RaidenUnrecoverableError(
                        "The secret registry address for the token network is invalid."
                    )

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

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

                total_supply = token_proxy.total_supply(
                    block_identifier=failed_at_blocknumber)
                if not total_supply or total_supply <= 0:
                    raise RaidenRecoverableError(
                        f"The given token address is not a valid ERC20 token, "
                        f"total_supply() returned an invalid value {total_supply}."
                    )

                # At this point, the TokenNetworkRegistry fails to instantiate
                # a new TokenNetwork.
                raise RaidenUnrecoverableError(
                    "createERC20TokenNetwork failed for an unknown reason, even "
                    "though the gas estimation succeeded.")

            succeeded_at_blockhash = receipt["blockHash"]
            token_network_address = self.get_token_network(
                token_address, succeeded_at_blockhash)
            if token_network_address is None:
                msg = "createERC20TokenNetwork succeeded but token network address is Null"
                raise RaidenUnrecoverableError(msg)
        else:  # `estimated_transaction` is None
            # The latest block can not be used reliably because of reorgs,
            # therefore every call using this block has to handle pruned data.
            failed_at_block = self.rpc_client.get_block(BLOCK_ID_LATEST)
            failed_at_blockhash = failed_at_block["hash"].hex()
            failed_at_blocknumber = failed_at_block["number"]

            max_token_networks = self.get_max_token_networks(
                block_identifier=failed_at_blocknumber)
            token_networks_created = self.get_token_network_created(
                block_identifier=failed_at_blocknumber)

            already_registered = self.get_token_network(
                token_address=token_address,
                block_identifier=failed_at_blocknumber)
            deprecation_executor = self.get_deprecation_executor(
                block_identifier=failed_at_blocknumber)
            settlement_timeout_min = self.settlement_timeout_min(
                block_identifier=failed_at_blocknumber)
            settlement_timeout_max = self.settlement_timeout_max(
                block_identifier=failed_at_blocknumber)
            chain_id = self.get_chain_id(
                block_identifier=failed_at_blocknumber)
            secret_registry_address = self.get_secret_registry_address(
                block_identifier=failed_at_blocknumber)

            try:
                # Creating a new instance to run the constructor checks.
                token_proxy = Token(
                    jsonrpc_client=self.rpc_client,
                    token_address=token_address,
                    contract_manager=self.proxy_manager.contract_manager,
                    block_identifier=failed_at_blocknumber,
                )
            except AddressWithoutCode:
                # This cannot be an unrecoverable error, since the ERC20
                # code is external.
                raise RaidenRecoverableError(
                    "Token disappeared! The address "
                    "{to_checksum_address(token_address)} did have code at "
                    "block {log_details['given_block_identifier']}, however "
                    "at block {failed_at_blocknumber} when the registration "
                    "transaction was mined the address didn't have code "
                    "anymore.")

            check_address_has_code_handle_pruned_block(
                client=self.rpc_client,
                address=Address(secret_registry_address),
                contract_name=CONTRACT_SECRET_REGISTRY,
                expected_code=decode_hex(
                    self.proxy_manager.contract_manager.get_runtime_hexcode(
                        CONTRACT_SECRET_REGISTRY)),
                given_block_identifier=failed_at_blocknumber,
            )

            required_gas = self.gas_measurements[
                "TokenNetworkRegistry createERC20TokenNetwork"]

            self.rpc_client.check_for_insufficient_eth(
                transaction_name="createERC20TokenNetwork",
                transaction_executed=False,
                required_gas=required_gas,
                block_identifier=failed_at_blocknumber,
            )

            if token_networks_created >= max_token_networks:
                raise RaidenRecoverableError(
                    "The number of existing token networks reached the maximum allowed"
                )

            if already_registered:
                # Race condition lost, the token network was created in a different
                # transaction which got mined first.
                raise RaidenRecoverableError(
                    "The token was already registered in the TokenNetworkRegistry."
                )

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

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

            if chain_id != self.rpc_client.chain_id:
                raise RaidenUnrecoverableError(
                    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 RaidenUnrecoverableError(
                    "The secret registry address for the token network is invalid."
                )

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

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

            total_supply = token_proxy.total_supply(
                block_identifier=failed_at_blocknumber)
            if not total_supply or total_supply <= 0:
                raise RaidenRecoverableError(
                    f"The given token address is not a valid ERC20 token, "
                    f"total_supply() returned an invalid value {total_supply}."
                )

            # At this point, the TokenNetworkRegistry fails to instantiate
            # a new TokenNetwork.
            raise RaidenUnrecoverableError(
                f"createERC20TokenNetwork gas estimation failed for an unknown "
                f"reason. Reference block {failed_at_blockhash} "
                f"{failed_at_blocknumber}.")
        return token_network_address
Пример #12
0
    def _add_token(
        self,
        token_address: TokenAddress,
        channel_participant_deposit_limit: TokenAmount,
        token_network_deposit_limit: TokenAmount,
        log_details: Dict[Any, Any],
    ) -> TokenNetworkAddress:
        token_network_address = None

        checking_block = self.rpc_client.get_checking_block()

        kwargs = {
            "_token_address": token_address,
            "_channel_participant_deposit_limit":
            channel_participant_deposit_limit,
            "_token_network_deposit_limit": token_network_deposit_limit,
        }
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            "createERC20TokenNetwork",
                                            **kwargs)

        if gas_limit:
            gas_limit = safe_gas_limit(
                gas_limit, self.gas_measurements[
                    "TokenNetworkRegistry createERC20TokenNetwork"])
            log_details["gas_limit"] = gas_limit
            transaction_hash = self.proxy.transact("createERC20TokenNetwork",
                                                   gas_limit, **kwargs)

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

            if failed_receipt:
                failed_at_blocknumber = failed_receipt["blockNumber"]

                max_token_networks = self.get_max_token_networks(
                    block_identifier=failed_at_blocknumber)
                token_networks_created = self.get_token_network_created(
                    block_identifier=failed_at_blocknumber)
                already_registered = self.get_token_network(
                    token_address=token_address,
                    block_identifier=failed_at_blocknumber)
                deprecation_executor = self.get_deprecation_executor(
                    block_identifier=failed_at_blocknumber)
                settlement_timeout_min = self.settlement_timeout_min(
                    block_identifier=failed_at_blocknumber)
                settlement_timeout_max = self.settlement_timeout_max(
                    block_identifier=failed_at_blocknumber)
                chain_id = self.get_chain_id(
                    block_identifier=failed_at_blocknumber)
                secret_registry_address = self.get_secret_registry_address(
                    block_identifier=failed_at_blocknumber)

                if failed_receipt["cumulativeGasUsed"] == gas_limit:
                    msg = (
                        f"createERC20TokenNetwork failed and all gas was used "
                        f"({gas_limit}). Estimate gas may have underestimated "
                        f"createERC20TokenNetwork, or succeeded even though an assert is "
                        f"triggered, or the smart contract code has an "
                        f"conditional assert.")
                    raise RaidenRecoverableError(msg)

                if token_networks_created >= max_token_networks:
                    raise RaidenRecoverableError(
                        "The number of existing token networks reached the maximum allowed"
                    )

                if already_registered:
                    # Race condition lost, the token network was created in a different
                    # transaction which got mined first.
                    raise RaidenRecoverableError(
                        "The token was already registered in the TokenNetworkRegistry."
                    )

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

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

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

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

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

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

                # At this point, the TokenNetworkRegistry fails to instantiate
                # a new TokenNetwork.
                raise RaidenUnrecoverableError(
                    "createERC20TokenNetwork failed for an unknown reason")

            token_network_address = self.get_token_network(
                token_address, receipt["blockHash"])
            if token_network_address is None:
                msg = "createERC20TokenNetwork succeeded but token network address is Null"
                raise RaidenUnrecoverableError(msg)
        else:
            # The latest block can not be used reliably because of reorgs,
            # therefore every call using this block has to handle pruned data.
            failed_at = self.proxy.jsonrpc_client.get_block("latest")
            failed_at_blocknumber = failed_at["number"]

            max_token_networks = self.get_max_token_networks(
                block_identifier=failed_at_blocknumber)
            token_networks_created = self.get_token_network_created(
                block_identifier=failed_at_blocknumber)

            already_registered = self.get_token_network(
                token_address=token_address,
                block_identifier=failed_at_blocknumber)
            deprecation_executor = self.get_deprecation_executor(
                block_identifier=failed_at_blocknumber)
            settlement_timeout_min = self.settlement_timeout_min(
                block_identifier=failed_at_blocknumber)
            settlement_timeout_max = self.settlement_timeout_max(
                block_identifier=failed_at_blocknumber)
            chain_id = self.get_chain_id(
                block_identifier=failed_at_blocknumber)
            secret_registry_address = self.get_secret_registry_address(
                block_identifier=failed_at_blocknumber)

            required_gas = (
                gas_limit if gas_limit else self.gas_measurements.get(
                    "TokenNetworkRegistry createERC20TokenNetwork"))
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name="createERC20TokenNetwork",
                transaction_executed=False,
                required_gas=required_gas,
                block_identifier=failed_at_blocknumber,
            )

            if token_networks_created >= max_token_networks:
                raise RaidenRecoverableError(
                    "The number of existing token networks reached the maximum allowed"
                )

            if already_registered:
                # Race condition lost, the token network was created in a different
                # transaction which got mined first.
                raise RaidenRecoverableError(
                    "The token was already registered in the TokenNetworkRegistry."
                )

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

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

            if chain_id != self.rpc_client.chain_id:
                raise RaidenUnrecoverableError(
                    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:
                raise RaidenUnrecoverableError(
                    "The secret registry address for the token network is invalid."
                )

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

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

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

            if self.get_token_network(token_address, failed_at_blocknumber):
                raise RaidenRecoverableError("Token already registered")

            # At this point, the TokenNetworkRegistry fails to instantiate
            # a new TokenNetwork.
            raise RaidenUnrecoverableError(
                "createERC20TokenNetwork failed for an unknown reason")
        return token_network_address
Пример #13
0
def _add_onchain_locksroot_to_channel_settled_state_changes(
    raiden: RaidenService,
    storage: SQLiteStorage,
) -> None:
    """ Adds `our_onchain_locksroot` and `partner_onchain_locksroot` to
    ContractReceiveChannelSettled. """
    batch_size = 50
    batch_query = storage.batch_query_state_changes(
        batch_size=batch_size,
        filters=[
            ('_type',
             'raiden.transfer.state_change.ContractReceiveChannelSettled'),
        ],
    )
    for state_changes_batch in batch_query:
        updated_state_changes = list()
        for state_change in state_changes_batch:
            state_change_data = json.loads(state_change.data)
            msg = 'v18 state changes cant contain our_onchain_locksroot'
            assert 'our_onchain_locksroot' not in state_change_data, msg

            msg = 'v18 state changes cant contain partner_onchain_locksroot'
            assert 'partner_onchain_locksroot' not in state_change_data, msg

            token_network_identifier = state_change_data[
                'token_network_identifier']
            channel_identifier = state_change_data['channel_identifier']

            channel_new_state_change = _find_channel_new_state_change(
                storage=storage,
                token_network_address=token_network_identifier,
                channel_identifier=channel_identifier,
            )

            if not channel_new_state_change.data:
                raise RaidenUnrecoverableError(
                    f'Could not find the state change for channel {channel_identifier}, '
                    f'token network address: {token_network_identifier} being created. ',
                )

            channel_state_data = json.loads(channel_new_state_change.data)
            new_channel_state = channel_state_data['channel_state']

            canonical_identifier = CanonicalIdentifier(
                chain_identifier=CHAIN_ID_UNSPECIFIED,
                token_network_address=to_canonical_address(
                    token_network_identifier),
                channel_identifier=int(channel_identifier),
            )
            our_locksroot, partner_locksroot = get_onchain_locksroots(
                chain=raiden.chain,
                canonical_identifier=canonical_identifier,
                participant1=to_canonical_address(
                    new_channel_state['our_state']['address']),
                participant2=to_canonical_address(
                    new_channel_state['partner_state']['address']),
                block_identifier='latest',
            )

            state_change_data['our_onchain_locksroot'] = serialize_bytes(
                our_locksroot, )
            state_change_data['partner_onchain_locksroot'] = serialize_bytes(
                partner_locksroot, )

            updated_state_changes.append((
                json.dumps(state_change_data),
                state_change.state_change_identifier,
            ))
        storage.update_state_changes(updated_state_changes)
Пример #14
0
    def approve(
        self,
        allowed_address: Address,
        allowance: TokenAmount,
        given_block_identifier: BlockSpecification,
    ):
        """ Aprove `allowed_address` to transfer up to `deposit` amount of token.

        Note:

            For channel deposit please use the channel proxy, since it does
            additional validations.
        """
        # Note that given_block_identifier is not used here as there
        # are no preconditions to check before sending the transaction

        log_details = {
            'node': pex(self.node_address),
            'contract': pex(self.address),
            'allowed_address': pex(allowed_address),
            'allowance': allowance,
        }

        checking_block = self.client.get_checking_block()
        error_prefix = 'Call to approve will fail'
        gas_limit = self.proxy.estimate_gas(
            checking_block,
            'approve',
            to_checksum_address(allowed_address),
            allowance,
        )

        if gas_limit:
            error_prefix = 'Call to approve failed'
            log.debug('approve called', **log_details)
            transaction_hash = self.proxy.transact(
                'approve',
                safe_gas_limit(gas_limit),
                to_checksum_address(allowed_address),
                allowance,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client,
                                                      transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = checking_block

            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='approve',
                transaction_executed=transaction_executed,
                required_gas=GAS_REQUIRED_FOR_APPROVE,
                block_identifier=block,
            )

            msg = self._check_why_approved_failed(allowance, block)
            error_msg = f'{error_prefix}. {msg}'
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        log.info('approve successful', **log_details)
Пример #15
0
    def update_transfer(
        self,
        channel_identifier: typing.ChannelID,
        partner: typing.Address,
        balance_hash: typing.BalanceHash,
        nonce: typing.Nonce,
        additional_hash: typing.AdditionalHash,
        closing_signature: typing.Signature,
        non_closing_signature: typing.Signature,
    ):
        log_details = {
            'token_network': pex(self.address),
            'node': pex(self.node_address),
            'partner': pex(partner),
            'nonce': nonce,
            'balance_hash': encode_hex(balance_hash),
            'additional_hash': encode_hex(additional_hash),
            'closing_signature': encode_hex(closing_signature),
            'non_closing_signature': encode_hex(non_closing_signature),
        }
        log.debug('updateNonClosingBalanceProof called', **log_details)

        self._check_for_outdated_channel(
            self.node_address,
            partner,
            channel_identifier,
        )

        transaction_hash = self.proxy.transact(
            'updateNonClosingBalanceProof',
            channel_identifier,
            partner,
            self.node_address,
            balance_hash,
            nonce,
            additional_hash,
            closing_signature,
            non_closing_signature,
        )

        self.client.poll(transaction_hash)

        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)
        if receipt_or_none:
            channel_closed = self.channel_is_closed(
                participant1=self.node_address,
                participant2=partner,
                channel_identifier=channel_identifier,
            )
            if channel_closed is False:
                msg = 'Channel is not in a closed state'
                log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                             **log_details)
                raise RaidenUnrecoverableError(msg)

            msg = 'Update NonClosing balance proof'
            log.critical(f'updateNonClosingBalanceProof failed, {msg}',
                         **log_details)
            raise TransactionThrew(msg, receipt_or_none)

        log.info('updateNonClosingBalanceProof successful', **log_details)
Пример #16
0
    def handle_contract_send_channelunlock(
            self,
            raiden: RaidenService,
            channel_unlock_event: ContractSendChannelBatchUnlock,
    ):
        token_network_identifier = channel_unlock_event.token_network_identifier
        channel_identifier = channel_unlock_event.channel_identifier
        participant = channel_unlock_event.participant
        token_address = channel_unlock_event.token_address

        payment_channel: PaymentChannel = raiden.chain.payment_channel(
            token_network_address=token_network_identifier,
            channel_id=channel_identifier,
        )
        token_network: TokenNetwork = payment_channel.token_network

        participants_details = token_network.detail_participants(
            participant1=raiden.address,
            participant2=participant,
            channel_identifier=channel_identifier,
        )

        our_details = participants_details.our_details
        our_locksroot = our_details.locksroot

        partner_details = participants_details.partner_details
        partner_locksroot = partner_details.locksroot

        is_partner_unlock = (
            partner_details.address == participant and
            partner_locksroot != EMPTY_HASH
        )
        is_our_unlock = (
            our_details.address == participant and
            our_locksroot != EMPTY_HASH
        )

        if is_partner_unlock:
            state_change_record = get_state_change_with_balance_proof(
                storage=raiden.wal.storage,
                chain_id=raiden.chain.network_id,
                token_network_identifier=token_network_identifier,
                channel_identifier=channel_identifier,
                balance_hash=partner_details.balance_hash,
                sender=participants_details.partner_details.address,
            )
            state_change_identifier = state_change_record.state_change_identifier
        elif is_our_unlock:
            event_record = get_event_with_balance_proof(
                storage=raiden.wal.storage,
                chain_id=raiden.chain.network_id,
                token_network_identifier=token_network_identifier,
                channel_identifier=channel_identifier,
                balance_hash=our_details.balance_hash,
            )
            state_change_identifier = event_record.state_change_identifier
        else:
            state_change_identifier = 0

        if not state_change_identifier:
            raise RaidenUnrecoverableError(
                f'Failed to find state/event that match current channel locksroots. '
                f'token:{to_checksum_address(token_address)} '
                f'token_network:{to_checksum_address(token_network_identifier)} '
                f'channel:{channel_identifier} '
                f'participant:{to_checksum_address(participant)} '
                f'our_locksroot:{to_hex(our_locksroot)} '
                f'partner_locksroot:{to_hex(partner_locksroot)} ',
            )

        # Replay state changes until a channel state is reached where
        # this channel state has the participants balance hash.
        restored_channel_state = channel_state_until_state_change(
            raiden=raiden,
            payment_network_identifier=raiden.default_registry.address,
            token_address=token_address,
            channel_identifier=channel_identifier,
            state_change_identifier=state_change_identifier,
        )

        our_state = restored_channel_state.our_state
        partner_state = restored_channel_state.partner_state
        if partner_state.address == participant:
            merkle_tree_leaves = get_batch_unlock(partner_state)
        elif our_state.address == participant:
            merkle_tree_leaves = get_batch_unlock(our_state)

        try:
            payment_channel.unlock(merkle_tree_leaves)
        except ChannelOutdatedError as e:
            log.error(
                str(e),
                node=pex(raiden.address),
            )
Пример #17
0
    def new_netting_channel(
        self,
        partner: typing.Address,
        settle_timeout: int,
    ) -> typing.ChannelID:
        """ Creates a new channel in the TokenNetwork contract.

        Args:
            partner: The peer to open the channel with.
            settle_timeout: The settle timeout to use for this channel.

        Returns:
            The ChannelID of the new netting channel.
        """
        if not is_binary_address(partner):
            raise InvalidAddress(
                'Expected binary address format for channel partner')

        invalid_timeout = (settle_timeout < self.settlement_timeout_min()
                           or settle_timeout > self.settlement_timeout_max())
        if invalid_timeout:
            raise InvalidSettleTimeout(
                'settle_timeout must be in range [{}, {}], is {}'.format(
                    self.settlement_timeout_min(),
                    self.settlement_timeout_max(),
                    settle_timeout,
                ))

        if self.node_address == partner:
            raise SamePeerAddress(
                'The other peer must not have the same address as the client.')

        log_details = {
            'peer1': pex(self.node_address),
            'peer2': pex(partner),
        }
        log.debug('new_netting_channel called', **log_details)

        # Prevent concurrent attempts to open a channel with the same token and
        # partner address.
        if partner not in self.open_channel_transactions:
            new_open_channel_transaction = AsyncResult()
            self.open_channel_transactions[
                partner] = new_open_channel_transaction

            try:
                transaction_hash = self._new_netting_channel(
                    partner, settle_timeout)
            except Exception as e:
                log.critical('new_netting_channel failed', **log_details)
                new_open_channel_transaction.set_exception(e)
                raise
            else:
                new_open_channel_transaction.set(transaction_hash)
            finally:
                self.open_channel_transactions.pop(partner, None)
        else:
            # All other concurrent threads should block on the result of opening this channel
            self.open_channel_transactions[partner].get()

        channel_created = self.channel_exists_and_not_settled(
            self.node_address, partner)
        if channel_created is False:
            log.critical('new_netting_channel failed', **log_details)
            raise RaidenUnrecoverableError('creating new channel failed')

        channel_identifier: typing.ChannelID = self.detail_channel(
            self.node_address,
            partner,
        ).channel_identifier
        log_details['channel_identifier'] = str(channel_identifier)
        log.info('new_netting_channel successful', **log_details)

        return channel_identifier
Пример #18
0
    def register_secret_batch(self, secrets: List[Secret]):
        """Register a batch of secrets. Check if they are already registered at
        the given block identifier."""
        secrets_to_register = list()
        secrethashes_to_register = list()
        secrethashes_not_sent = list()
        transaction_result = AsyncResult()
        wait_for = set()

        # secret registration has no preconditions:
        #
        # - The action does not depend on any state, it's always valid to call
        #   it.
        # - This action is always susceptible to race conditions.
        #
        # Therefore this proxy only needs to detect if the secret is already
        # registered, to avoid sending obviously unecessary transactions, and
        # it has to handle race conditions.

        with self._open_secret_transactions_lock:
            verification_block_hash = self.client.get_confirmed_blockhash()

            for secret in secrets:
                secrethash = sha3(secret)
                secrethash_hex = encode_hex(secrethash)

                # Do the local test on `open_secret_transactions` first, then
                # if necessary do an RPC call.
                #
                # The call to `is_secret_registered` has two conflicting
                # requirements:
                #
                # - Avoid sending duplicated transactions for the same lock
                # - Operating on a consistent/confirmed view of the blockchain
                #   (if a secret has been registered in a block that is not
                #   confirmed it doesn't count yet, an optimization would be to
                #   *not* send the transaction and wait for the confirmation)
                #
                # The code below respects the consistent blockchain view,
                # meaning that if this proxy method is called with an old
                # blockhash an unecessary transaction will be sent, and the
                # error will be treated as a race-condition.
                other_result = self.open_secret_transactions.get(secret)

                if other_result is not None:
                    wait_for.add(other_result)
                    secrethashes_not_sent.append(secrethash_hex)
                elif not self.is_secret_registered(secrethash,
                                                   verification_block_hash):
                    secrets_to_register.append(secret)
                    secrethashes_to_register.append(secrethash_hex)
                    self.open_secret_transactions[secret] = transaction_result

        # From here on the lock is not required. Context-switches will happen
        # for the gas estimation and the transaction, however the
        # synchronization data is limited to the open_secret_transactions
        log_details = {
            "node": pex(self.node_address),
            "contract": pex(self.address),
            "secrethashes": secrethashes_to_register,
            "secrethashes_not_sent": secrethashes_not_sent,
        }

        if not secrets_to_register:
            log.debug("registerSecretBatch skipped, waiting for transactions",
                      **log_details)

            gevent.joinall(wait_for, raise_error=True)

            log.info("registerSecretBatch successful", **log_details)
            return

        checking_block = self.client.get_checking_block()
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            "registerSecretBatch",
                                            secrets_to_register)
        receipt = None
        transaction_hash = None
        msg = None

        if gas_limit:
            gas_limit = safe_gas_limit(
                gas_limit,
                len(secrets_to_register) * GAS_REQUIRED_PER_SECRET_IN_BATCH)

            log.debug("registerSecretBatch called", **log_details)

            try:
                transaction_hash = self.proxy.transact("registerSecretBatch",
                                                       gas_limit,
                                                       secrets_to_register)
                self.client.poll(transaction_hash)
                receipt = self.client.get_transaction_receipt(transaction_hash)
            except Exception as e:  # pylint: disable=broad-except
                msg = f"Unexpected exception {e} at sending registerSecretBatch transaction."

        # Clear `open_secret_transactions` regardless of the transaction being
        # successfully executed or not.
        with self._open_secret_transactions_lock:
            for secret in secrets_to_register:
                self.open_secret_transactions.pop(secret)

        # As of version `0.4.0` of the contract has *no* asserts or requires.
        # Therefore the only reason for the transaction to fail is if there is
        # a bug.
        unrecoverable_error = (gas_limit is None or receipt is None
                               or receipt["status"] == RECEIPT_FAILURE_CODE)

        exception: Union[RaidenRecoverableError, RaidenUnrecoverableError]
        if unrecoverable_error:
            # If the transaction was sent it must not fail. If this happened
            # some of our assumptions is broken therefore the error is
            # unrecoverable
            if receipt is not None:
                if receipt["gasUsed"] == gas_limit:
                    # The transaction failed and all gas was used. This can
                    # happen because of:
                    #
                    # - A compiler bug if an invalid opcode was executed.
                    # - A configuration bug if an assert was executed,
                    # because version 0.4.0 of the secret registry does not have an
                    # assert.
                    # - An ethereum client bug if the gas_limit was
                    # underestimated.
                    #
                    # Safety cannot be guaranteed under any of these cases,
                    # this error is unrecoverable.
                    error = (
                        "Secret registration failed because of a bug in either "
                        "the solidity compiler, the running ethereum client, or "
                        "a configuration error in Raiden.")
                else:
                    # The transaction failed and *not* all gas was used. This
                    # can happen because of:
                    #
                    # - A compiler bug if a revert was introduced.
                    # - A configuration bug, because for 0.4.0 the secret
                    # registry does not have a revert.
                    error = (
                        "Secret registration failed because of a configuration "
                        "bug or compiler bug. Please double check the secret "
                        "smart contract is at version 0.4.0, if it is then a "
                        "compiler bug was hit.")

                log.critical(error, **log_details)
                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # If gas_limit is set and there is no receipt then an exception was
            # raised while sending the transaction. This should only happen if
            # the account is being used concurrently, which is not supported.
            # This can happen because:
            #
            # - The nonce of the transaction was already used
            # - The nonce was reused *and* the account didn't have enough ether
            # to pay for the gas
            #
            # Safety cannot be guaranteed under any of these cases, this error
            # is unrecoverable. *Note*: This assumes the ethereum client
            # takes into account the current transactions in the pool.
            if gas_limit:
                assert msg, "Unexpected control flow, an exception should have been raised."
                error = (
                    f"Sending the the transaction for registerSecretBatch failed with: `{msg}`. "
                    f"This happens if the same ethereum account is being used by more than one "
                    f"program which is not supported.")

                log.critical(error, **log_details)
                exception = RaidenUnrecoverableError(error)
                transaction_result.set_exception(exception)
                raise exception

            # gas_limit can fail because:
            #
            # - The Ethereum client detected the transaction could not
            # successfully execute, this happens if an assert/revert is hit.
            # - The account is lacking funds to pay for the gas.
            #
            # Either of these is a bug. The contract does not use
            # assert/revert, and the account should always be funded
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name="registerSecretBatch",
                address=self.node_address,
                transaction_executed=True,
                required_gas=gas_limit,
                block_identifier=checking_block,
            )
            error = "Call to registerSecretBatch couldn't be done"

            log.critical(error, **log_details)
            exception = RaidenRecoverableError(error)
            transaction_result.set_exception(exception)
            raise exception

        # The local **MUST** transaction_result be set before waiting for the
        # other results, otherwise we have a dead-lock
        transaction_result.set(transaction_hash)

        if wait_for:
            log.info("registerSecretBatch waiting for pending", **log_details)
            gevent.joinall(wait_for, raise_error=True)

        log.info("registerSecretBatch successful", **log_details)
Пример #19
0
    def unlock(
        self,
        channel_identifier: typing.ChannelID,
        partner: typing.Address,
        merkle_tree_leaves: typing.MerkleTreeLeaves,
    ):
        log_details = {
            'token_network': pex(self.address),
            'node': pex(self.node_address),
            'partner': pex(partner),
            'merkle_tree_leaves': merkle_tree_leaves,
        }

        if merkle_tree_leaves is None or not merkle_tree_leaves:
            log.info('skipping unlock, tree is empty', **log_details)
            return

        log.info('unlock called', **log_details)

        leaves_packed = b''.join(lock.encoded for lock in merkle_tree_leaves)

        gas_limit = self.proxy.estimate_gas(
            'unlock',
            channel_identifier,
            self.node_address,
            partner,
            leaves_packed,
        )
        gas_limit = safe_gas_limit(gas_limit, UNLOCK_TX_GAS_LIMIT)

        transaction_hash = self.proxy.transact(
            'unlock',
            gas_limit,
            channel_identifier,
            self.node_address,
            partner,
            leaves_packed,
        )

        self.client.poll(transaction_hash)
        receipt_or_none = check_transaction_threw(self.client,
                                                  transaction_hash)

        if receipt_or_none:
            channel_settled = self.channel_is_settled(
                participant1=self.node_address,
                participant2=partner,
                channel_identifier=channel_identifier,
            )

            if channel_settled is False:
                log.critical(
                    'unlock failed. Channel is not in a settled state',
                    **log_details)
                raise RaidenUnrecoverableError(
                    'Channel is not in a settled state. An unlock cannot be made',
                )

            log.critical('unlock failed', **log_details)
            raise TransactionThrew('Unlock', receipt_or_none)

        log.info('unlock successful', **log_details)
Пример #20
0
 def __del__(self) -> None:
     if not self._sent:
         raise RaidenUnrecoverableError(
             f"Transaction nonce {self.nonce} was not used! "
             f"This will result in nonce synchronization problems.")
Пример #21
0
    def handle_contract_send_channelsettle(
        self,
        raiden: RaidenService,
        channel_settle_event: ContractSendChannelSettle,
    ):
        chain_id = raiden.chain.network_id
        token_network_identifier = channel_settle_event.token_network_identifier
        channel_identifier = channel_settle_event.channel_identifier

        payment_channel: PaymentChannel = raiden.chain.payment_channel(
            token_network_address=channel_settle_event.
            token_network_identifier,
            channel_id=channel_settle_event.channel_identifier,
        )

        token_network_proxy: TokenNetwork = payment_channel.token_network
        participants_details = token_network_proxy.detail_participants(
            participant1=payment_channel.participant1,
            participant2=payment_channel.participant2,
            block_identifier='latest',
            channel_identifier=channel_settle_event.channel_identifier,
        )

        our_details = participants_details.our_details
        partner_details = participants_details.partner_details

        log_details = {
            'chain_id': chain_id,
            'token_network_identifier': token_network_identifier,
            'channel_identifier': channel_identifier,
            'node': pex(raiden.address),
            'partner': to_checksum_address(partner_details.address),
            'our_deposit': our_details.deposit,
            'our_withdrawn': our_details.withdrawn,
            'our_is_closer': our_details.is_closer,
            'our_balance_hash': to_hex(our_details.balance_hash),
            'our_nonce': our_details.nonce,
            'our_locksroot': to_hex(our_details.locksroot),
            'our_locked_amount': our_details.locked_amount,
            'partner_deposit': partner_details.deposit,
            'partner_withdrawn': partner_details.withdrawn,
            'partner_is_closer': partner_details.is_closer,
            'partner_balance_hash': to_hex(partner_details.balance_hash),
            'partner_nonce': partner_details.nonce,
            'partner_locksroot': to_hex(partner_details.locksroot),
            'partner_locked_amount': partner_details.locked_amount,
        }

        if our_details.balance_hash != EMPTY_HASH:
            event_record = get_event_with_balance_proof_by_balance_hash(
                storage=raiden.wal.storage,
                chain_id=chain_id,
                token_network_identifier=token_network_identifier,
                channel_identifier=channel_identifier,
                balance_hash=our_details.balance_hash,
            )

            if event_record.data is None:
                log.critical(
                    'our balance proof not found',
                    **log_details,
                )
                raise RaidenUnrecoverableError(
                    'Our balance proof could not be found in the database', )

            our_balance_proof = event_record.data.balance_proof
            our_transferred_amount = our_balance_proof.transferred_amount
            our_locked_amount = our_balance_proof.locked_amount
            our_locksroot = our_balance_proof.locksroot
        else:
            our_transferred_amount = 0
            our_locked_amount = 0
            our_locksroot = EMPTY_HASH

        if partner_details.balance_hash != EMPTY_HASH:
            state_change_record = get_state_change_with_balance_proof_by_balance_hash(
                storage=raiden.wal.storage,
                chain_id=chain_id,
                token_network_identifier=token_network_identifier,
                channel_identifier=channel_identifier,
                balance_hash=partner_details.balance_hash,
                sender=participants_details.partner_details.address,
            )
            if state_change_record.data is None:
                log.critical(
                    'partner balance proof not found',
                    **log_details,
                )
                raise RaidenUnrecoverableError(
                    'Partner balance proof could not be found in the database',
                )

            partner_balance_proof = state_change_record.data.balance_proof
            partner_transferred_amount = partner_balance_proof.transferred_amount
            partner_locked_amount = partner_balance_proof.locked_amount
            partner_locksroot = partner_balance_proof.locksroot
        else:
            partner_transferred_amount = 0
            partner_locked_amount = 0
            partner_locksroot = EMPTY_HASH

        # LEFTODO: Supply a proper block id
        payment_channel.settle(
            transferred_amount=our_transferred_amount,
            locked_amount=our_locked_amount,
            locksroot=our_locksroot,
            partner_transferred_amount=partner_transferred_amount,
            partner_locked_amount=partner_locked_amount,
            partner_locksroot=partner_locksroot,
            block_identifier='latest',
        )
Пример #22
0
    def start_mediated_transfer_with_secret(
        self,
        token_network_identifier: TokenNetworkID,
        amount: PaymentAmount,
        fee: FeeAmount,
        target: TargetAddress,
        identifier: PaymentID,
        secret: Secret,
        secrethash: SecretHash = None,
    ) -> PaymentStatus:

        if secrethash is None:
            secrethash = sha3(secret)

        # We must check if the secret was registered against the latest block,
        # even if the block is forked away and the transaction that registers
        # the secret is removed from the blockchain. The rationale here is that
        # someone else does know the secret, regardless of the chain state, so
        # the node must not use it to start a payment.
        #
        # For this particular case, it's preferable to use `latest` instead of
        # having a specific block_hash, because it's preferable to know if the secret
        # was ever known, rather than having a consistent view of the blockchain.
        secret_registered = self.default_secret_registry.is_secret_registered(
            secrethash=secrethash, block_identifier="latest")
        if secret_registered:
            raise RaidenUnrecoverableError(
                f"Attempted to initiate a locked transfer with secrethash {pex(secrethash)}."
                f" That secret is already registered onchain.")

        self.start_health_check_for(Address(target))

        if identifier is None:
            identifier = create_default_identifier()

        with self.payment_identifier_lock:
            payment_status = self.targets_to_identifiers_to_statuses[
                target].get(identifier)
            if payment_status:
                payment_status_matches = payment_status.matches(
                    token_network_identifier, amount)
                if not payment_status_matches:
                    raise PaymentConflict(
                        "Another payment with the same id is in flight")

                return payment_status

            payment_status = PaymentStatus(
                payment_identifier=identifier,
                amount=amount,
                token_network_identifier=token_network_identifier,
                payment_done=AsyncResult(),
            )
            self.targets_to_identifiers_to_statuses[target][
                identifier] = payment_status

        init_initiator_statechange = initiator_init(
            raiden=self,
            transfer_identifier=identifier,
            transfer_amount=amount,
            transfer_secret=secret,
            transfer_secrethash=secrethash,
            transfer_fee=fee,
            token_network_identifier=token_network_identifier,
            target_address=target,
        )

        # Dispatch the state change even if there are no routes to create the
        # wal entry.
        self.handle_and_track_state_change(init_initiator_statechange)

        return payment_status
Пример #23
0
    def deposit(
        self,
        beneficiary: Address,
        total_deposit: TokenAmount,
        block_identifier: BlockSpecification,
    ) -> None:
        """ Deposit provided amount into the user-deposit contract
        to the beneficiary's account. """

        token_address = self.token_address(block_identifier)
        token = Token(
            jsonrpc_client=self.client,
            token_address=token_address,
            contract_manager=self.contract_manager,
        )

        log_details = {
            'beneficiary': pex(beneficiary),
            'contract': pex(self.address),
            'total_deposit': total_deposit,
        }

        checking_block = self.client.get_checking_block()
        error_prefix = 'Call to deposit will fail'

        with self.deposit_lock:
            amount_to_deposit, log_details = self._deposit_preconditions(
                total_deposit=total_deposit,
                beneficiary=beneficiary,
                token=token,
                block_identifier=block_identifier,
            )
            gas_limit = self.proxy.estimate_gas(
                checking_block,
                'deposit',
                to_checksum_address(beneficiary),
                total_deposit,
            )

            if gas_limit:
                error_prefix = 'Call to deposit failed'
                log.debug('deposit called', **log_details)
                transaction_hash = self.proxy.transact(
                    'deposit',
                    safe_gas_limit(gas_limit),
                    to_checksum_address(beneficiary),
                    total_deposit,
                )

                self.client.poll(transaction_hash)
                receipt_or_none = check_transaction_threw(
                    self.client, transaction_hash)

            transaction_executed = gas_limit is not None
            if not transaction_executed or receipt_or_none:
                if transaction_executed:
                    block = receipt_or_none['blockNumber']
                else:
                    block = checking_block

                self.proxy.jsonrpc_client.check_for_insufficient_eth(
                    transaction_name='deposit',
                    transaction_executed=transaction_executed,
                    required_gas=GAS_REQUIRED_FOR_UDC_DEPOSIT,
                    block_identifier=block,
                )

                msg = self._check_why_deposit_failed(
                    token=token,
                    amount_to_deposit=amount_to_deposit,
                    total_deposit=total_deposit,
                    block_identifier=block,
                )
                error_msg = f'{error_prefix}. {msg}'
                log.critical(error_msg, **log_details)
                raise RaidenUnrecoverableError(error_msg)

        log.info('deposit successful', **log_details)
Пример #24
0
    def handle_contract_send_channelunlock(
        raiden: "RaidenService",
        chain_state: ChainState,
        channel_unlock_event: ContractSendChannelBatchUnlock,
    ) -> None:
        assert raiden.wal, "The Raiden Service must be initialize to handle events"

        canonical_identifier = channel_unlock_event.canonical_identifier
        token_network_address = canonical_identifier.token_network_address
        channel_identifier = canonical_identifier.channel_identifier
        participant = channel_unlock_event.sender

        channel_state = get_channelstate_by_canonical_identifier(
            chain_state=state_from_raiden(raiden), canonical_identifier=canonical_identifier
        )
        if channel_state is None:
            raise RaidenUnrecoverableError(
                "ContractSendChannelBatchUnlock for non-existing channel."
            )

        confirmed_block_identifier = state_from_raiden(raiden).block_hash
        payment_channel: PaymentChannel = raiden.proxy_manager.payment_channel(
            channel_state=channel_state, block_identifier=confirmed_block_identifier
        )

        channel_state = get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_address=token_network_address,
            partner_address=participant,
        )

        if not channel_state:
            # channel was cleaned up already due to an unlock
            raise RaidenUnrecoverableError(
                f"Failed to find channel state with partner:"
                f"{to_checksum_address(participant)}, "
                f"token_network:{to_checksum_address(token_network_address)}"
            )

        our_address = channel_state.our_state.address
        our_locksroot = channel_state.our_state.onchain_locksroot

        partner_address = channel_state.partner_state.address
        partner_locksroot = channel_state.partner_state.onchain_locksroot

        # we want to unlock because there are on-chain unlocked locks
        search_events = our_locksroot != LOCKSROOT_OF_NO_LOCKS
        # we want to unlock, because there are unlocked/unclaimed locks
        search_state_changes = partner_locksroot != LOCKSROOT_OF_NO_LOCKS

        if not search_events and not search_state_changes:
            # In the case that someone else sent the unlock we do nothing
            # Check https://github.com/raiden-network/raiden/issues/3152
            # for more details
            log.warning(
                "Onchain unlock already mined",
                canonical_identifier=canonical_identifier,
                channel_identifier=canonical_identifier.channel_identifier,
                participant=to_checksum_address(participant),
            )
            return

        if search_state_changes:
            state_change_record = get_state_change_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                locksroot=partner_locksroot,
                sender=partner_address,
            )

            if state_change_record is None:
                raise RaidenUnrecoverableError(
                    f"Failed to find state that matches the current channel locksroots. "
                    f"chain_id:{raiden.rpc_client.chain_id} "
                    f"token_network:{to_checksum_address(token_network_address)} "
                    f"channel:{channel_identifier} "
                    f"participant:{to_checksum_address(participant)} "
                    f"our_locksroot:{to_hex(our_locksroot)} "
                    f"partner_locksroot:{to_hex(partner_locksroot)} "
                )

            state_change_identifier = state_change_record.state_change_identifier
            restored_channel_state = channel_state_until_state_change(
                raiden=raiden,
                canonical_identifier=canonical_identifier,
                state_change_identifier=state_change_identifier,
            )

            gain = get_batch_unlock_gain(restored_channel_state)

            skip_unlock = (
                restored_channel_state.partner_state.address == participant
                and gain.from_partner_locks == 0
            )
            if not skip_unlock:
                unlock(
                    payment_channel=payment_channel,
                    end_state=restored_channel_state.partner_state,
                    sender=partner_address,
                    receiver=our_address,
                    given_block_identifier=channel_unlock_event.triggered_by_block_hash,
                )

        if search_events:
            event_record = get_event_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                locksroot=our_locksroot,
                recipient=partner_address,
            )

            if event_record is None:
                raise RaidenUnrecoverableError(
                    f"Failed to find event that match current channel locksroots. "
                    f"chain_id:{raiden.rpc_client.chain_id} "
                    f"token_network:{to_checksum_address(token_network_address)} "
                    f"channel:{channel_identifier} "
                    f"participant:{to_checksum_address(participant)} "
                    f"our_locksroot:{to_hex(our_locksroot)} "
                    f"partner_locksroot:{to_hex(partner_locksroot)} "
                )

            state_change_identifier = event_record.state_change_identifier
            restored_channel_state = channel_state_until_state_change(
                raiden=raiden,
                canonical_identifier=canonical_identifier,
                state_change_identifier=state_change_identifier,
            )

            gain = get_batch_unlock_gain(restored_channel_state)

            skip_unlock = (
                restored_channel_state.our_state.address == participant
                and gain.from_our_locks == 0
            )
            if not skip_unlock:
                try:
                    unlock(
                        payment_channel=payment_channel,
                        end_state=restored_channel_state.our_state,
                        sender=our_address,
                        receiver=partner_address,
                        given_block_identifier=channel_unlock_event.triggered_by_block_hash,
                    )
                except InsufficientEth as e:
                    raise RaidenUnrecoverableError(str(e)) from e