From 1151e56d844cd92de54a23841f4f1894bfb3f50f Mon Sep 17 00:00:00 2001 From: Fernando Cezar Bernardelli Date: Tue, 29 Jun 2021 15:46:47 +0200 Subject: [PATCH] include and forward encrypted secret in locked transfer message --- raiden/message_handler.py | 10 ++- raiden/transfer/channel.py | 143 +++++++++++++++++++++---------------- raiden/transfer/utils.py | 25 ++++++- 3 files changed, 111 insertions(+), 67 deletions(-) diff --git a/raiden/message_handler.py b/raiden/message_handler.py index ed10fb62c2..6964057c7a 100644 --- a/raiden/message_handler.py +++ b/raiden/message_handler.py @@ -4,7 +4,7 @@ from gevent.pool import Pool from raiden.constants import ABSENT_SECRET, BLOCK_ID_LATEST -from raiden.exceptions import ServiceRequestFailed +from raiden.exceptions import InvalidSecret, ServiceRequestFailed from raiden.messages.abstract import Message from raiden.messages.decode import balanceproof_from_envelope, lockedtransfersigned_from_message from raiden.messages.synchronization import Delivered, Processed @@ -40,6 +40,7 @@ ReceiveWithdrawExpired, ReceiveWithdrawRequest, ) +from raiden.transfer.utils import decrypt_secret from raiden.transfer.views import TransferRole from raiden.utils.transfers import random_secret from raiden.utils.typing import ( @@ -338,6 +339,13 @@ def handle_message_lockedtransfer( sender = from_transfer.balance_proof.sender if message.target == TargetAddress(raiden.address): + encrypted_secret = getattr(message.metadata, "encrypt_secret", None) + if encrypted_secret: + try: + secret = decrypt_secret(encrypted_secret, raiden.rpc_client.privkey) + return [ReceiveSecretReveal(secret=secret, sender=message.sender)] + except InvalidSecret: + log.error("Ignoring invalid encrypted secret") return [ ActionInitTarget( from_hop=from_hop, diff --git a/raiden/transfer/channel.py b/raiden/transfer/channel.py index c17ad8bf37..d2db434be6 100644 --- a/raiden/transfer/channel.py +++ b/raiden/transfer/channel.py @@ -79,7 +79,7 @@ ReceiveWithdrawExpired, ReceiveWithdrawRequest, ) -from raiden.transfer.utils import hash_balance_data +from raiden.transfer.utils import FuncMap, hash_balance_data from raiden.utils.formatting import to_checksum_address from raiden.utils.packing import pack_balance_proof, pack_withdraw from raiden.utils.signer import recover @@ -2548,68 +2548,85 @@ def state_transition( channel_state, events ) - if type(state_change) == Block: - assert isinstance(state_change, Block), MYPY_ANNOTATION - iteration = handle_block( - channel_state, state_change, block_number, pseudo_random_generator - ) - elif type(state_change) == ActionChannelClose: - assert isinstance(state_change, ActionChannelClose), MYPY_ANNOTATION - iteration = handle_action_close( - channel_state=channel_state, - close=state_change, - block_number=block_number, - block_hash=block_hash, - ) - elif type(state_change) == ActionChannelWithdraw: - assert isinstance(state_change, ActionChannelWithdraw), MYPY_ANNOTATION - iteration = handle_action_withdraw( - channel_state=channel_state, - action_withdraw=state_change, - pseudo_random_generator=pseudo_random_generator, - block_number=block_number, - ) - elif type(state_change) == ActionChannelSetRevealTimeout: - assert isinstance(state_change, ActionChannelSetRevealTimeout), MYPY_ANNOTATION - iteration = handle_action_set_reveal_timeout( - channel_state=channel_state, state_change=state_change - ) - elif type(state_change) == ContractReceiveChannelClosed: - assert isinstance(state_change, ContractReceiveChannelClosed), MYPY_ANNOTATION - iteration = handle_channel_closed(channel_state, state_change) - elif type(state_change) == ContractReceiveUpdateTransfer: - assert isinstance(state_change, ContractReceiveUpdateTransfer), MYPY_ANNOTATION - iteration = handle_channel_updated_transfer(channel_state, state_change, block_number) - elif type(state_change) == ContractReceiveChannelSettled: - assert isinstance(state_change, ContractReceiveChannelSettled), MYPY_ANNOTATION - iteration = handle_channel_settled(channel_state, state_change) - elif type(state_change) == ContractReceiveChannelDeposit: - assert isinstance(state_change, ContractReceiveChannelDeposit), MYPY_ANNOTATION - iteration = handle_channel_deposit(channel_state, state_change) - elif type(state_change) == ContractReceiveChannelBatchUnlock: - assert isinstance(state_change, ContractReceiveChannelBatchUnlock), MYPY_ANNOTATION - iteration = handle_channel_batch_unlock(channel_state, state_change) - elif type(state_change) == ContractReceiveChannelWithdraw: - assert isinstance(state_change, ContractReceiveChannelWithdraw), MYPY_ANNOTATION - iteration = handle_channel_withdraw(channel_state=channel_state, state_change=state_change) - elif type(state_change) == ReceiveWithdrawRequest: - assert isinstance(state_change, ReceiveWithdrawRequest), MYPY_ANNOTATION - iteration = handle_receive_withdraw_request( - channel_state=channel_state, withdraw_request=state_change - ) - elif type(state_change) == ReceiveWithdrawConfirmation: - assert isinstance(state_change, ReceiveWithdrawConfirmation), MYPY_ANNOTATION - iteration = handle_receive_withdraw_confirmation( - channel_state=channel_state, - withdraw=state_change, - block_number=block_number, - block_hash=block_hash, - ) - elif type(state_change) == ReceiveWithdrawExpired: - assert isinstance(state_change, ReceiveWithdrawExpired), MYPY_ANNOTATION - iteration = handle_receive_withdraw_expired( - channel_state=channel_state, withdraw_expired=state_change, block_number=block_number - ) + transition_map: Dict[Any, FuncMap] = { + Block: FuncMap( + handle_block, (channel_state, state_change, block_number, pseudo_random_generator), {} + ), + ActionChannelClose: FuncMap( + handle_action_close, + (), + dict( + channel_state=channel_state, + close=state_change, + block_number=block_number, + block_hash=block_hash, + ), + ), + ActionChannelWithdraw: FuncMap( + handle_action_withdraw, + (), + dict( + channel_state=channel_state, + action_withdraw=state_change, + pseudo_random_generator=pseudo_random_generator, + block_number=block_number, + ), + ), + ActionChannelSetRevealTimeout: FuncMap( + handle_action_set_reveal_timeout, + (), + dict(channel_state=channel_state, state_change=state_change), + ), + ContractReceiveChannelClosed: FuncMap( + handle_channel_closed, (channel_state, state_change), {} + ), + ContractReceiveUpdateTransfer: FuncMap( + handle_channel_updated_transfer, (channel_state, state_change, block_number), {} + ), + ContractReceiveChannelSettled: FuncMap( + handle_channel_settled, (channel_state, state_change), {} + ), + ContractReceiveChannelDeposit: FuncMap( + handle_channel_deposit, (channel_state, state_change), {} + ), + ContractReceiveChannelBatchUnlock: FuncMap( + handle_channel_batch_unlock, (channel_state, state_change), {} + ), + ContractReceiveChannelWithdraw: FuncMap( + handle_channel_withdraw, + (), + dict(channel_state=channel_state, state_change=state_change), + ), + ReceiveWithdrawRequest: FuncMap( + handle_receive_withdraw_request, + (), + dict(channel_state=channel_state, withdraw_request=state_change), + ), + ReceiveWithdrawConfirmation: FuncMap( + handle_receive_withdraw_confirmation, + (), + dict( + channel_state=channel_state, + withdraw=state_change, + block_number=block_number, + block_hash=block_hash, + ), + ), + ReceiveWithdrawExpired: FuncMap( + handle_receive_withdraw_expired, + (), + dict( + channel_state=channel_state, + withdraw_expired=state_change, + block_number=block_number, + ), + ), + } + + t_state_change = type(state_change) + func_map = transition_map.get(t_state_change) + if func_map: + iteration = func_map.function(*func_map.args, **func_map.kwargs) # type: ignore if iteration.new_state is not None: sanity_check(iteration.new_state) diff --git a/raiden/transfer/utils.py b/raiden/transfer/utils.py index cde1747d51..58f1c52564 100644 --- a/raiden/transfer/utils.py +++ b/raiden/transfer/utils.py @@ -1,27 +1,31 @@ import random +from dataclasses import dataclass, field from random import Random from typing import TYPE_CHECKING -from ecies import encrypt +from ecies import decrypt, encrypt from eth_hash.auto import keccak - from eth_utils import decode_hex from raiden.constants import EMPTY_HASH, LOCKSROOT_OF_NO_LOCKS - +from raiden.exceptions import InvalidSecret from raiden.utils.signer import get_public_key from raiden.utils.typing import ( AddressMetadata, Any, BalanceHash, + Callable, + Dict, EncryptedSecret, LockedAmount, Locksroot, Optional, + PrivateKey, Secret, SecretHash, Signature, TokenAmount, + Tuple, Union, ) @@ -31,6 +35,13 @@ from raiden.transfer.state_change import ContractReceiveSecretReveal # noqa: F401 +@dataclass +class FuncMap: + function: Callable + args: Optional[Tuple] = field(default_factory=tuple) + kwargs: Optional[Dict[Any, Any]] = field(default_factory=dict) + + def hash_balance_data( transferred_amount: TokenAmount, locked_amount: LockedAmount, locksroot: Locksroot ) -> BalanceHash: @@ -79,3 +90,11 @@ def encrypt_secret( if public_key: encrypted_secret = EncryptedSecret(encrypt(public_key.to_hex(), secret)) return encrypted_secret + + +def decrypt_secret(encrypted_secret: EncryptedSecret, private_key: PrivateKey) -> Secret: + try: + secret = Secret(decrypt(encrypted_secret, private_key)) + except ValueError: + raise InvalidSecret + return secret