Skip to content

Unclear handling of exceptions thrown in proxy's set_total_withdraw #4942

@LefterisJP

Description

@LefterisJP

Problem Definition

While working on #4937 I noticed that the rest api calls the token network's set_total_withdraw() in an asynchronous way. Thus unlike for other proxy calls any exceptions thrown inside the proxy are not handled.

EDIT: Read the issue to figure out why this is actually not the case but also why this is still problematic. Keeping the description of what I initially thought to be a bug below so that the train of though of how we came here is preserved.

raiden/raiden/api/rest.py

Lines 1154 to 1165 in 6983c90

try:
self.raiden_api.set_total_channel_withdraw(
registry_address,
channel_state.token_address,
channel_state.partner_state.address,
total_withdraw,
)
except (NonexistingChannel, UnknownTokenAddress) as e:
return api_error(errors=str(e), status_code=HTTPStatus.BAD_REQUEST)
except (InsufficientFunds, WithdrawMismatch) as e:
return api_error(errors=str(e), status_code=HTTPStatus.CONFLICT)

Here the rest api calls the python api's set_total_channel_withdraw

raiden/raiden/api/python.py

Lines 555 to 564 in 6983c90

self.raiden.withdraw(
canonical_identifier=channel_state.canonical_identifier, total_withdraw=total_withdraw
)
waiting.wait_for_withdraw_complete(
raiden=self.raiden,
canonical_identifier=channel_state.canonical_identifier,
total_withdraw=total_withdraw,
retry_timeout=retry_timeout,
)

In there instead of calling the proxy directly what the code does is call raiden_service.withdraw()

def withdraw(
self, canonical_identifier: CanonicalIdentifier, total_withdraw: WithdrawAmount
) -> None:
init_withdraw = ActionChannelWithdraw(
canonical_identifier=canonical_identifier, total_withdraw=total_withdraw
)
self.handle_and_track_state_changes([init_withdraw])

Which as can be seen above creates an Action state change and adds it to the pending greenlets for processing.

def send_withdraw_request(
channel_state: NettingChannelState,
total_withdraw: WithdrawAmount,
block_number: BlockNumber,
pseudo_random_generator: random.Random,
) -> List[Event]:
events: List[Event] = list()
if get_status(channel_state) not in CHANNEL_STATES_PRIOR_TO_CLOSED:
return events
nonce = get_next_nonce(channel_state.our_state)
expiration = get_safe_initial_expiration(
block_number=block_number, reveal_timeout=channel_state.reveal_timeout
)
withdraw_state = PendingWithdrawState(
total_withdraw=total_withdraw, nonce=nonce, expiration=expiration
)
channel_state.our_state.nonce = nonce
channel_state.our_state.withdraws_pending[withdraw_state.total_withdraw] = withdraw_state
withdraw_event = SendWithdrawRequest(
canonical_identifier=CanonicalIdentifier(
chain_identifier=channel_state.chain_id,
token_network_address=channel_state.token_network_address,
channel_identifier=channel_state.identifier,
),
recipient=channel_state.partner_state.address,
message_identifier=message_identifier_from_prng(pseudo_random_generator),
total_withdraw=withdraw_state.total_withdraw,
participant=channel_state.our_state.address,
nonce=channel_state.our_state.nonce,
expiration=withdraw_state.expiration,
)
events.append(withdraw_event)
return events

That results in processing the Action through the state machine and eventually generating a SendWithdrawRequest event as seen above.

This will send a WithdrawRequest to the partner .... wait for the confirmation ... process it ... and only during that processing will the proxy call be done. So the proxy's exceptions will not be seen.

Task

Figure out a way to do this properly and catch the exceptions.

@staticmethod
def handle_contract_send_channelwithdraw(
raiden: "RaidenService", channel_withdraw_event: ContractSendChannelWithdraw
) -> None:
withdraw_confirmation_data = pack_withdraw(
canonical_identifier=channel_withdraw_event.canonical_identifier,
participant=raiden.address,
total_withdraw=channel_withdraw_event.total_withdraw,
expiration_block=channel_withdraw_event.expiration,
)
our_signature = raiden.signer.sign(data=withdraw_confirmation_data)
channel_proxy = raiden.chain.payment_channel(
canonical_identifier=channel_withdraw_event.canonical_identifier
)
channel_proxy.set_total_withdraw(
total_withdraw=channel_withdraw_event.total_withdraw,
expiration_block=channel_withdraw_event.expiration,
participant_signature=our_signature,
partner_signature=channel_withdraw_event.partner_signature,
block_identifier=channel_withdraw_event.triggered_by_block_hash,
)

Perhaps catch the exceptions in the handler of the ContractSendChannelWithdraw event?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions