Skip to content

Commit

Permalink
include and forward encrypted secret in locked transfer message
Browse files Browse the repository at this point in the history
  • Loading branch information
Fernando Cezar Bernardelli committed Jul 27, 2021
1 parent 66b1814 commit 858bb8e
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 69 deletions.
24 changes: 22 additions & 2 deletions raiden/message_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -40,11 +40,13 @@
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 (
TYPE_CHECKING,
AddressMetadata,
Any,
List,
Optional,
Set,
Expand Down Expand Up @@ -338,14 +340,32 @@ def handle_message_lockedtransfer(
sender = from_transfer.balance_proof.sender

if message.target == TargetAddress(raiden.address):
return [
actions: List[Any] = [
ActionInitTarget(
from_hop=from_hop,
transfer=from_transfer,
balance_proof=balance_proof,
sender=sender,
valid_secret_received=True,
)
]
encrypted_secret = getattr(message.metadata, "encrypt_secret", None)
if encrypted_secret:
try:
secret = decrypt_secret(encrypted_secret, raiden.rpc_client.privkey)
actions.append(ReceiveSecretReveal(secret=secret, sender=message.sender))
except InvalidSecret:
actions = [
ActionInitTarget(
from_hop=from_hop,
transfer=from_transfer,
balance_proof=balance_proof,
sender=sender,
valid_secret_received=False,
)
]
log.error("Ignoring invalid encrypted secret")
return actions
else:
filtered_route_states = list()
for route_state in from_transfer.route_states:
Expand Down
143 changes: 80 additions & 63 deletions raiden/transfer/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions raiden/transfer/mediated_transfer/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from raiden.utils.typing import (
Address,
BlockExpiration,
EncryptedSecret,
List,
PaymentAmount,
PaymentID,
Expand Down Expand Up @@ -126,6 +127,13 @@ class EventUnlockSuccess(Event):
secrethash: SecretHash


@dataclass(frozen=True)
class EventDecryptSecretReveal(Event):
"""Event by a target when an encrypted secret is present in the transaction."""

encrypt_secret: EncryptedSecret


@dataclass(frozen=True)
class EventUnlockFailed(Event):
"""Event emitted when a lock unlock failed."""
Expand Down
1 change: 1 addition & 0 deletions raiden/transfer/mediated_transfer/state_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class ActionInitTarget(BalanceProofStateChange):

from_hop: HopState
transfer: LockedTransferSignedState
valid_secret_received: bool = field(default=False)

def __post_init__(self) -> None:
super().__post_init__()
Expand Down
4 changes: 4 additions & 0 deletions raiden/transfer/mediated_transfer/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ def handle_inittarget(
)

if is_valid:

if state_change.valid_secret_received:
return TransitionResult(None, [])

# A valid balance proof does not mean the payment itself is still valid.
# e.g. the lock may be near expiration or have expired. This is fine. The
# message with an unusable lock must be handled to properly synchronize the
Expand Down
25 changes: 22 additions & 3 deletions raiden/transfer/utils.py
Original file line number Diff line number Diff line change
@@ -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,
)

Expand All @@ -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:
Expand Down Expand Up @@ -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
8 changes: 7 additions & 1 deletion requirements/requirements-ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ appdirs==1.4.3
# via
# -r requirements-dev.txt
# black
appnope==0.1.2
# via
# -r requirements-dev.txt
# ipython
asn1crypto==1.3.0
# via
# -r requirements-dev.txt
Expand Down Expand Up @@ -357,7 +361,9 @@ lru-dict==1.1.6
# -r requirements-dev.txt
# web3
macholib==1.14
# via -r requirements-ci.in
# via
# -r requirements-ci.in
# pyinstaller
markupsafe==2.0.1
# via
# -r requirements-dev.txt
Expand Down
2 changes: 2 additions & 0 deletions requirements/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ apipkg==1.5
# via execnet
appdirs==1.4.3
# via black
appnope==0.1.2
# via ipython
asn1crypto==1.3.0
# via
# -r requirements.txt
Expand Down

0 comments on commit 858bb8e

Please sign in to comment.