diff --git a/raiden/api/python.py b/raiden/api/python.py index 43edf20eb0..374534409e 100644 --- a/raiden/api/python.py +++ b/raiden/api/python.py @@ -542,10 +542,7 @@ def close(self, token_address, partner_address): if channel.received_transfers: first_transfer = channel.received_transfers[-1] - netting_channel = channel.external_state.netting_channel - netting_channel.close( - first_transfer, - ) + channel.external_state.close(first_transfer) return channel diff --git a/raiden/channel/netting_channel.py b/raiden/channel/netting_channel.py index 401b5ba18a..4652ce4493 100644 --- a/raiden/channel/netting_channel.py +++ b/raiden/channel/netting_channel.py @@ -100,10 +100,48 @@ def query_settled(self): def close(self, partner_transfer): if not self._called_close: self._called_close = True - return self.netting_channel.close(partner_transfer) + + if partner_transfer: + nonce = partner_transfer.nonce + transferred_amount = partner_transfer.transferred_amount + locksroot = partner_transfer.locksroot + signature = partner_transfer.signature + + packed = partner_transfer.packed() + message_hash = sha3(packed.data[:-65]) + + else: + nonce = 0 + transferred_amount = 0 + locksroot = '' + signature = '' + message_hash = '' + + return self.netting_channel.close( + nonce, + transferred_amount, + locksroot, + message_hash, + signature, + ) def update_transfer(self, partner_transfer): - return self.netting_channel.update_transfer(partner_transfer) + if partner_transfer: + nonce = partner_transfer.nonce + transferred_amount = partner_transfer.transferred_amount + locksroot = partner_transfer.locksroot + signature = partner_transfer.signature + + packed = partner_transfer.packed() + message_hash = sha3(packed.data[:-65]) + + return self.netting_channel.update_transfer( + nonce, + transferred_amount, + locksroot, + message_hash, + signature, + ) def withdraw(self, unlock_proofs): return self.netting_channel.withdraw(unlock_proofs) @@ -640,6 +678,7 @@ def create_directtransfer(self, amount, identifier): identifier=identifier, nonce=from_.nonce, token=self.token_address, + channel=self.channel_address, transferred_amount=transferred_amount, recipient=to_.address, locksroot=current_locksroot, @@ -675,6 +714,7 @@ def create_lockedtransfer(self, amount, identifier, expiration, hashlock): identifier=identifier, nonce=from_.nonce, token=self.token_address, + channel=self.channel_address, transferred_amount=transferred_amount, recipient=to_.address, locksroot=updated_locksroot, diff --git a/raiden/encoding/format.py b/raiden/encoding/format.py index d4cfdcfd9c..256d6aa28d 100644 --- a/raiden/encoding/format.py +++ b/raiden/encoding/format.py @@ -98,6 +98,11 @@ def namedbuffer(buffer_name, fields_spec): # noqa (ignore ciclomatic complexity names_slices = compute_slices(fields_spec) sorted_names = sorted(names_fields.keys()) + @staticmethod + def get_bytes_from(buffer_, name): + slice_ = names_slices[name] + return buffer_[slice_] + def __init__(self, data): if len(data) != size: raise ValueError('data buffer has the wrong size, expected {}'.format(size)) @@ -173,6 +178,7 @@ def __dir__(self): 'fields_spec': fields_spec, 'format': fields_format, 'size': size, + 'get_bytes_from': get_bytes_from, } return type(buffer_name, (), attributes) diff --git a/raiden/encoding/messages.py b/raiden/encoding/messages.py index 1f1d8eb77d..c39d4b7d27 100644 --- a/raiden/encoding/messages.py +++ b/raiden/encoding/messages.py @@ -11,7 +11,6 @@ namedbuffer, pad, ) -from raiden.encoding.signing import recover_publickey def to_bigendian(number): @@ -69,6 +68,7 @@ def make_message(message, **attrs): target = make_field('target', 20, '20s') initiator = make_field('initiator', 20, '20s') sender = make_field('sender', 20, '20s') +channel = make_field('channel', 20, '20s') locksroot = make_field('locksroot', 32, '32s') hashlock = make_field('hashlock', 32, '32s') @@ -140,12 +140,13 @@ def make_message(message, **attrs): DirectTransfer = namedbuffer( 'direct_transfer', [ - cmdid(DIRECTTRANSFER), # [0:1] - pad(3), # [1:4] - nonce, # [4:12] - identifier, # [12:20] - token, # [20:40] - recipient, # [40:60] + cmdid(DIRECTTRANSFER), + pad(3), + nonce, + identifier, + token, + channel, + recipient, transferred_amount, optional_locksroot, signature, @@ -155,42 +156,44 @@ def make_message(message, **attrs): MediatedTransfer = namedbuffer( 'mediated_transfer', [ - cmdid(MEDIATEDTRANSFER), # [0:1] - pad(3), # [1:4] - nonce, # [4:12] - identifier, # [12:20] - expiration, # [20:28] - token, # [28:48] - recipient, # [48:68] - target, # [68:88] - initiator, # [88:108] - locksroot, # [108:140] - hashlock, # [140:172] - transferred_amount, # [172:204] - amount, # [204:236] - fee, # [236:268] - signature, # [268:333] + cmdid(MEDIATEDTRANSFER), + pad(3), + nonce, + identifier, + expiration, + token, + channel, + recipient, + target, + initiator, + locksroot, + hashlock, + transferred_amount, + amount, + fee, + signature, ] ) RefundTransfer = namedbuffer( 'refund_transfer', [ - cmdid(REFUNDTRANSFER), # [0:1] - pad(3), # [1:4] - nonce, # [4:12] - identifier, # [12:20] - expiration, # [20:28] - token, # [28:48] - recipient, # [48:68] - target, # [68:88] - initiator, # [88:108] - locksroot, # [108:140] - hashlock, # [140:172] - transferred_amount, # [172:204] - amount, # [204:236] - fee, # [236:268] - signature, # [268:333] + cmdid(REFUNDTRANSFER), + pad(3), + nonce, + identifier, + expiration, + token, + channel, + recipient, + target, + initiator, + locksroot, + hashlock, + transferred_amount, + amount, + fee, + signature, ] ) @@ -216,51 +219,6 @@ def make_message(message, **attrs): } -def wrap_and_validate(data): - ''' Try to decode data into a message and validate the signature, might - return None if the data is invalid. - ''' - try: - first_byte = data[0] - except KeyError: - log.warn('data is empty') - return - - try: - message_type = CMDID_MESSAGE[first_byte] - except KeyError: - log.error('unknown cmdid %s', first_byte) - return - - try: - message = message_type(data) - except ValueError: - log.error('trying to decode invalid message') - return - - assert message_type.fields_spec[-1].name == 'signature', 'signature is not the last field' - # this slice must be from the end of the buffer - message_data = message.data[:-signature.size_bytes] - message_signature = message.data[-signature.size_bytes:] - - try: - publickey = recover_publickey(message_data, message_signature) - except ValueError: - # raised if the signature has the wrong length - log.error('invalid signature') - return - except TypeError as e: - # raised if the PublicKey instantiation failed - log.error('invalid key data: {}'.format(e.message)) - return - except Exception as e: - # secp256k1 is using bare Exception classes: raised if the recovery failed - log.error('error while recovering pubkey: {}'.format(e.message)) - return - - return message, publickey - - def wrap(data): ''' Try to decode data into a message, might return None if the data is invalid. ''' try: diff --git a/raiden/messages.py b/raiden/messages.py index e16e4f6a93..2a04687c51 100644 --- a/raiden/messages.py +++ b/raiden/messages.py @@ -4,6 +4,7 @@ from raiden.encoding import messages, signing from raiden.encoding.format import buffer_for +from raiden.encoding.signing import recover_publickey from raiden.utils import publickey_to_address, sha3, ishash, pex __all__ = ( @@ -21,7 +22,7 @@ log = getLogger(__name__) # pylint: disable=invalid-name -def assert_transfer_values(identifier, nonce, token, transferred_amount, recipient): +def assert_transfer_values(identifier, nonce, token, channel, transferred_amount, recipient): if identifier < 0: raise ValueError('identifier cannot be negative') @@ -34,6 +35,9 @@ def assert_transfer_values(identifier, nonce, token, transferred_amount, recipie if nonce >= 2 ** 64: raise ValueError('nonce is too large') + if len(channel) != 20: + raise ValueError('channel is an invalid address') + if len(token) != 20: raise ValueError('token is an invalid address') @@ -127,14 +131,106 @@ def sign(self, private_key, node_address): @classmethod def decode(cls, data): - result = messages.wrap_and_validate(data) + packed = messages.wrap(data) + + if packed is None: + return + + # signature must be at the end + message_type = type(packed) + signature = message_type.fields_spec[-1] + assert signature.name == 'signature', 'signature is not the last field' + + data_that_was_signed = data[:-signature.size_bytes] + message_signature = data[-signature.size_bytes:] + + try: + publickey = recover_publickey(data_that_was_signed, message_signature) + except ValueError: + # raised if the signature has the wrong length + log.error('invalid signature') + return + except TypeError as e: + # raised if the PublicKey instantiation failed + log.error('invalid key data: {}'.format(e.message)) + return + except Exception as e: # pylint: disable=broad-except + # secp256k1 is using bare Exception classes: raised if the recovery failed + log.error('error while recovering pubkey: {}'.format(e.message)) + return + + message = cls.unpack(packed) # pylint: disable=no-member + message.sender = publickey_to_address(publickey) + return message + + +class EnvelopeMessage(SignedMessage): + def sign(self, private_key, node_address): + packed = self.packed() + klass = type(packed) + + field = klass.fields_spec[-1] + assert field.name == 'signature', 'signature is not the last field' + + data = packed.data + message_data = data[:-field.size_bytes] + message_hash = sha3(message_data) + + nonce = klass.get_bytes_from(data, 'nonce') + transferred_amount = klass.get_bytes_from(data, 'transferred_amount') + locksroot = klass.get_bytes_from(data, 'locksroot') + channel_address = klass.get_bytes_from(data, 'channel') + + data_to_sign = nonce + transferred_amount + locksroot + channel_address + message_hash + signature = signing.sign(data_to_sign, private_key) + + packed.signature = signature + + self.sender = node_address + self.signature = signature + + @classmethod + def decode(cls, data): + packed = messages.wrap(data) + + if packed is None: + return + + # signature must be at the end + message_type = type(packed) + signature = message_type.fields_spec[-1] + assert signature.name == 'signature', 'signature is not the last field' + + message_data = data[:-signature.size_bytes] + message_signature = data[-signature.size_bytes:] + message_hash = sha3(message_data) + + nonce = message_type.get_bytes_from(data, 'nonce') + transferred_amount = message_type.get_bytes_from(data, 'transferred_amount') + locksroot = message_type.get_bytes_from(data, 'locksroot') + channel_address = message_type.get_bytes_from(data, 'channel') - if result is None: + data_that_was_signed = ( + nonce + transferred_amount + locksroot + channel_address + message_hash + ) + + try: + publickey = recover_publickey(data_that_was_signed, message_signature) + except ValueError: + # raised if the signature has the wrong length + log.error('invalid signature') + return + except TypeError as e: + # raised if the PublicKey instantiation failed + log.error('invalid key data: {}'.format(e.message)) + return + except Exception as e: # pylint: disable=broad-except + # secp256k1 is using bare Exception classes: raised if the recovery failed + log.error('error while recovering pubkey: {}'.format(e.message)) return - packed, public_key = result message = cls.unpack(packed) # pylint: disable=no-member - message.sender = publickey_to_address(public_key) + message.sender = publickey_to_address(publickey) return message @@ -301,7 +397,7 @@ def pack(self, packed): packed.signature = self.signature -class DirectTransfer(SignedMessage): +class DirectTransfer(EnvelopeMessage): """ A direct token exchange, used when both participants have a previously opened channel. @@ -328,11 +424,21 @@ class DirectTransfer(SignedMessage): cmdid = messages.DIRECTTRANSFER - def __init__(self, identifier, nonce, token, transferred_amount, recipient, locksroot): + def __init__( + self, + identifier, + nonce, + token, + channel, + transferred_amount, + recipient, + locksroot): + assert_transfer_values( identifier, nonce, token, + channel, transferred_amount, recipient, ) @@ -341,6 +447,7 @@ def __init__(self, identifier, nonce, token, transferred_amount, recipient, lock self.identifier = identifier self.nonce = nonce self.token = token + self.channel = channel self.transferred_amount = transferred_amount #: total amount of token sent to partner self.recipient = recipient #: partner's address self.locksroot = locksroot #: the merkle root that represent all pending locked transfers @@ -351,6 +458,7 @@ def unpack(packed): packed.identifier, packed.nonce, packed.token, + packed.channel, packed.transferred_amount, packed.recipient, packed.locksroot, @@ -363,6 +471,7 @@ def pack(self, packed): packed.identifier = self.identifier packed.nonce = self.nonce packed.token = self.token + packed.channel = self.channel packed.transferred_amount = self.transferred_amount packed.recipient = self.recipient packed.locksroot = self.locksroot @@ -434,7 +543,7 @@ def __ne__(self, other): return not self.__eq__(other) -class LockedTransfer(SignedMessage): +class LockedTransfer(EnvelopeMessage): """ A transfer which signs that the partner can claim `locked_amount` if she knows the secret to `hashlock`. @@ -449,13 +558,23 @@ class LockedTransfer(SignedMessage): any signed [nonce, token, balance, recipient, locksroot, ...] along a merkle proof from locksroot to the not yet netted formerly locked amount """ - def __init__(self, identifier, nonce, token, transferred_amount, recipient, locksroot, lock): + def __init__( + self, + identifier, + nonce, + token, + channel, + transferred_amount, + recipient, + locksroot, + lock): super(LockedTransfer, self).__init__() assert_transfer_values( identifier, nonce, token, + channel, transferred_amount, recipient, ) @@ -463,6 +582,7 @@ def __init__(self, identifier, nonce, token, transferred_amount, recipient, lock self.identifier = identifier self.nonce = nonce self.token = token + self.channel = channel self.transferred_amount = transferred_amount self.recipient = recipient self.locksroot = locksroot @@ -473,6 +593,7 @@ def to_mediatedtransfer(self, target, initiator='', fee=0): self.identifier, self.nonce, self.token, + self.channel, self.transferred_amount, self.recipient, self.locksroot, @@ -487,6 +608,7 @@ def to_refundtransfer(self, target, initiator='', fee=0): self.identifier, self.nonce, self.token, + self.channel, self.transferred_amount, self.recipient, self.locksroot, @@ -508,6 +630,7 @@ def unpack(packed): packed.identifier, packed.nonce, packed.token, + packed.channel, packed.transferred_amount, packed.recipient, packed.locksroot, @@ -520,6 +643,7 @@ def pack(self, packed): packed.identifier = self.identifier packed.nonce = self.nonce packed.token = self.token + packed.channel = self.channel packed.transferred_amount = self.transferred_amount packed.recipient = self.recipient packed.locksroot = self.locksroot @@ -560,6 +684,7 @@ def __init__( identifier, nonce, token, + channel, transferred_amount, recipient, locksroot, @@ -581,6 +706,7 @@ def __init__( identifier, nonce, token, + channel, transferred_amount, recipient, locksroot, @@ -618,6 +744,7 @@ def unpack(packed): packed.identifier, packed.nonce, packed.token, + packed.channel, packed.transferred_amount, packed.recipient, packed.locksroot, @@ -633,6 +760,7 @@ def pack(self, packed): packed.identifier = self.identifier packed.nonce = self.nonce packed.token = self.token + packed.channel = self.channel packed.transferred_amount = self.transferred_amount packed.recipient = self.recipient packed.locksroot = self.locksroot @@ -667,6 +795,7 @@ def unpack(packed): packed.identifier, packed.nonce, packed.token, + packed.channel, packed.transferred_amount, packed.recipient, packed.locksroot, diff --git a/raiden/network/rpc/client.py b/raiden/network/rpc/client.py index de09aaf8a6..522dd3d182 100644 --- a/raiden/network/rpc/client.py +++ b/raiden/network/rpc/client.py @@ -985,51 +985,93 @@ def closing_address(self): def settled(self): return self.proxy.settled.call() - def close(self, their_transfer): - if their_transfer: - their_encoded = their_transfer.encode() - else: - their_encoded = '' - + def close(self, nonce, transferred_amount, locksroot, extra_hash, signature): transaction_hash = estimate_and_transact( self, self.proxy.close, - their_encoded, + nonce, + transferred_amount, + locksroot, + extra_hash, + signature, ) + try: - self.client.poll(transaction_hash.decode('hex'), timeout=self.poll_timeout) - except JSONRPCPollTimeoutException as e: - raise e - except InvalidTransaction as e: - raise e - log.info( - 'close called', - contract=pex(self.address), - their_transfer=their_transfer, - ) + log.info( + 'closing channel', + contract=pex(self.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, + ) - def update_transfer(self, their_transfer): - if their_transfer is not None: - their_transfer_encoded = their_transfer.encode() + self.client.poll( + transaction_hash.decode('hex'), + timeout=self.poll_timeout, + ) + except (InvalidTransaction, JSONRPCPollTimeoutException): + log.critical( + 'close failed', + contract=pex(self.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, + ) + raise + else: + log.info( + 'close sucessfull', + contract=pex(self.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, + ) + def update_transfer(self, nonce, transferred_amount, locksroot, extra_hash, signature): + if signature: transaction_hash = estimate_and_transact( self, self.proxy.updateTransfer, - their_transfer_encoded, + nonce, + transferred_amount, + locksroot, + extra_hash, + signature, ) try: - self.client.poll(transaction_hash.decode('hex'), timeout=self.poll_timeout) - except JSONRPCPollTimeoutException as e: - raise e - except InvalidTransaction as e: - raise e - - log.info( - 'update_transfer called', - contract=pex(self.address), - their_transfer=their_transfer, - ) + self.client.poll( + transaction_hash.decode('hex'), + timeout=self.poll_timeout, + ) + except (InvalidTransaction, JSONRPCPollTimeoutException): + log.critical( + 'updateTransfer failed', + contract=pex(self.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, + ) + + raise + else: + log.info( + 'updateTransfer sucessfull', + contract=pex(self.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, + ) # TODO: check if the ChannelSecretRevealed event was emitted and if # it wasn't raise an error diff --git a/raiden/smart_contracts/NettingChannelContract.sol b/raiden/smart_contracts/NettingChannelContract.sol index b166ee2f0a..0c5b915453 100644 --- a/raiden/smart_contracts/NettingChannelContract.sol +++ b/raiden/smart_contracts/NettingChannelContract.sol @@ -72,18 +72,39 @@ contract NettingChannelContract { } /// @notice Close the channel. Can only be called by a participant in the channel. - /// @param theirs_encoded The last transfer recieved from our partner. - function close(bytes theirs_encoded) { - data.close(theirs_encoded); + function close( + uint64 nonce, + uint256 transferred_amount, + bytes32 locksroot, + bytes32 extra_hash, + bytes signature + ) { + data.close( + nonce, + transferred_amount, + locksroot, + extra_hash, + signature + ); ChannelClosed(msg.sender, data.closed); } /// @notice Dispute the state after closing, called by the counterparty (the /// participant who did not close the channel). - /// @param theirs_encoded The transfer the counterparty believes is the valid - /// state of the first participant. - function updateTransfer(bytes theirs_encoded) { - data.updateTransfer(theirs_encoded); + function updateTransfer( + uint64 nonce, + uint256 transferred_amount, + bytes32 locksroot, + bytes32 extra_hash, + bytes signature + ) { + data.updateTransfer( + nonce, + transferred_amount, + locksroot, + extra_hash, + signature + ); TransferUpdated(msg.sender, block.number); } diff --git a/raiden/smart_contracts/NettingChannelLibrary.sol b/raiden/smart_contracts/NettingChannelLibrary.sol index a2b4cc4db1..f962e6e34d 100644 --- a/raiden/smart_contracts/NettingChannelLibrary.sol +++ b/raiden/smart_contracts/NettingChannelLibrary.sol @@ -62,16 +62,6 @@ library NettingChannelLibrary { _; } - function isValidNonce(Data storage self, uint64 nonce) - private - returns (bool) - { - return ( - nonce >= self.opened * (2 ** 32) && - nonce < (self.opened + 1) * (2 ** 32) - ); - } - /// @notice Deposit amount to channel. /// @dev Deposit an amount to the channel. At least one of the participants /// must deposit before the channel is opened. @@ -103,20 +93,17 @@ library NettingChannelLibrary { } /// @notice Close a channel between two parties that was used bidirectionally - /// @param their_transfer The latest known transfer of the other participant - /// to the channel. Can also be empty, in which case - /// we are attempting to close a channel without any - /// transfers. - function close(Data storage self, bytes their_transfer) - { + function close( + Data storage self, + uint64 nonce, + uint256 transferred_amount, + bytes32 locksroot, + bytes32 extra_hash, + bytes signature + ) { + address transfer_address; uint closer_index; uint counterparty_index; - bytes memory transfer_raw; - uint64 nonce; - address transfer_address; - address recipient; - bytes32 locksroot; - uint256 transferred_amount; // close can be called only once require(self.closed == 0); @@ -134,45 +121,40 @@ library NettingChannelLibrary { // he is intentionally not providing the latest transfer, in which case // the closing party is going to lose the tokens that were transferred // to him. - if (their_transfer.length != 0) { - (transfer_raw, transfer_address) = getTransferRawAddress(their_transfer); - counterparty_index = index_or_throw(self, transfer_address); + if (signature.length == 65) { + transfer_address = recoverAddressFromSignature( + nonce, + transferred_amount, + locksroot, + extra_hash, + signature + ); - // only a message from the counter party is valid + counterparty_index = index_or_throw(self, transfer_address); require(closer_index != counterparty_index); // update the structure of the counterparty with its data provided // by the closing node Participant storage counterparty = self.participants[counterparty_index]; - - (nonce, recipient, locksroot, transferred_amount) = decodeTransfer(transfer_raw); - - // only accept messages with a valid nonce - require(isValidNonce(self, nonce)); - - // the registered message recipient should be the closing party - require(recipient == self.closing_address); - - counterparty.nonce = nonce; + counterparty.nonce = uint64(nonce); counterparty.locksroot = locksroot; counterparty.transferred_amount = transferred_amount; } - } /// @notice Updates counter party transfer after closing. - /// @param their_transfer The transfer the counterparty believes is the - /// valid state for the first participant. - function updateTransfer(Data storage self, bytes their_transfer) + function updateTransfer( + Data storage self, + uint64 nonce, + uint256 transferred_amount, + bytes32 locksroot, + bytes32 extra_hash, + bytes signature + ) notSettledButClosed(self) stillTimeout(self) { address transfer_address; - address recipient; - bytes32 locksroot; - bytes memory transfer_raw; - uint256 transferred_amount; - uint64 nonce; uint8 caller_index; uint8 closer_index; @@ -186,31 +168,50 @@ library NettingChannelLibrary { // The closer is not allowed to call updateTransfer require(self.closing_address != msg.sender); - (transfer_raw, transfer_address) = getTransferRawAddress(their_transfer); - // Counter party can only update the closer transfer + transfer_address = recoverAddressFromSignature( + nonce, + transferred_amount, + locksroot, + extra_hash, + signature + ); require(transfer_address == self.closing_address); // Update the structure of the closer with its data provided by the // counterparty closer_index = 1 - caller_index; - (nonce, recipient, locksroot, transferred_amount) = decodeTransfer(transfer_raw); - - // only accept messages with a valid nonce - require(isValidNonce(self, nonce)); - - // the registered recipient in the message should be us - // Note: could have taken msg.sender here but trying to be future-proof - // for when we allow third party updates - Participant storage updating_party = self.participants[caller_index]; - require(updating_party.node_address == recipient); - self.participants[closer_index].nonce = nonce; self.participants[closer_index].locksroot = locksroot; self.participants[closer_index].transferred_amount = transferred_amount; } + function recoverAddressFromSignature( + uint64 nonce, + uint256 transferred_amount, + bytes32 locksroot, + bytes32 extra_hash, + bytes signature + ) + constant internal returns (address) + { + bytes32 signed_hash; + + require(signature.length == 65); + + signed_hash = sha3( + nonce, + transferred_amount, + locksroot, + this, + extra_hash + ); + + var (r, s, v) = signatureSplit(signature); + return ecrecover(signed_hash, v, r, s); + } + /// @notice Unlock a locked transfer /// @dev Unlock a locked transfer /// @param locked_encoded The lock @@ -350,26 +351,6 @@ library NettingChannelLibrary { kill(self); } - function getTransferRawAddress(bytes memory signed_transfer) internal returns (bytes memory, address) { - uint signature_start; - uint length; - bytes memory signature; - bytes memory transfer_raw; - bytes32 transfer_hash; - address transfer_address; - - length = signed_transfer.length; - signature_start = length - 65; - signature = slice(signed_transfer, signature_start, length); - transfer_raw = slice(signed_transfer, 0, signature_start); - - transfer_hash = sha3(transfer_raw); - var (r, s, v) = signatureSplit(signature); - transfer_address = ecrecover(transfer_hash, v, r, s); - - return (transfer_raw, transfer_address); - } - // NOTES: // // - The EVM is a big-endian, byte addressing machine, with 32bytes/256bits @@ -412,104 +393,6 @@ library NettingChannelLibrary { // - https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI // - http://solidity.readthedocs.io/en/develop/assembly.html - function decodeTransfer(bytes transfer_raw) - internal - returns (uint64 nonce, address recipient, bytes32 locksroot, uint256 transferred_amount) - { - uint cmdid = uint(transfer_raw[0]); - - if (cmdid == 5) { - return decodeDirectTransfer(transfer_raw); - } else if (cmdid == 7) { - return decodeMediatedTransfer(transfer_raw); - } else if (cmdid == 8) { - return decodeRefundTransfer(transfer_raw); - } - - revert(); - } - - function decodeDirectTransfer(bytes memory message) - private - returns (uint64 nonce, address recipient, bytes32 locksroot, uint256 transferred_amount) - { - // size of the raw message without the signature - require(message.length == 124); - - // Message format: - // [0:1] cmdid - // [1:4] pad - // [4:12] nonce - // [12:20] identifier - // [20:40] token - // [40:60] recipient - // [60:92] transferred_amount - // [92:124] optional_locksroot - assembly { - nonce := mload(add(message, 12)) - recipient := mload(add(message, 60)) - transferred_amount := mload(add(message, 92)) - locksroot := mload(add(message, 124)) - } - } - - function decodeMediatedTransfer(bytes memory message) - private - returns (uint64 nonce, address recipient, bytes32 locksroot, uint256 transferred_amount) - { - // size of the raw message without the signature - require(message.length == 268); - - // Message format: - // [0:1] cmdid - // [1:4] pad - // [4:12] nonce - // [12:20] identifier - // [20:28] expiration - // [28:48] token - // [48:68] recipient - // [68:88] target - // [88:108] initiator - // [108:140] locksroot - // [140:172] hashlock - // [172:204] transferred_amoun - // [204:236] amount - // [236:268] fee - assembly { - nonce := mload(add(message, 12)) - recipient := mload(add(message, 68)) - locksroot := mload(add(message, 140)) - transferred_amount := mload(add(message, 204)) - } - } - - function decodeRefundTransfer(bytes memory message) - private - returns (uint64 nonce, address recipient, bytes32 locksroot, uint256 transferred_amount) - { - // size of the raw message without the signature - require(message.length == 268); - - // Message format: - // [0:1] cmdid - // [1:4] pad - // [4:12] nonce - // [12:20] identifier - // [20:28] expiration - // [28:48] token - // [48:68] recipient - // [68:100] locksroot - // [100:132] transferred_amount - // [132:164] amount - // [164:196] hashlock - assembly { - nonce := mload(add(message, 12)) - recipient := mload(add(message, 68)) - locksroot := mload(add(message, 140)) - transferred_amount := mload(add(message, 204)) - } - } - function decodeLock(bytes lock) internal returns (uint64 expiration, uint amount, bytes32 hashlock) { require(lock.length == 72); @@ -542,16 +425,6 @@ library NettingChannelLibrary { require(v == 27 || v == 28); } - function slice(bytes a, uint start, uint end) private returns (bytes n) { - assert(end <= a.length); - assert(start < end); - - n = new bytes(end - start); - for (uint i = start; i < end; i++) { //python style slice - n[i - start] = a[i]; - } - } - function index_or_throw(Data storage self, address participant_address) private returns (uint8) { uint8 n; // Return index of participant, or throw diff --git a/raiden/tests/fixtures/tester.py b/raiden/tests/fixtures/tester.py index 115fa35871..ccef11b01a 100644 --- a/raiden/tests/fixtures/tester.py +++ b/raiden/tests/fixtures/tester.py @@ -250,7 +250,7 @@ def tester_channels(tester_state, tester_nettingcontracts, reveal_timeout): first_externalstate = ChannelExternalStateTester( tester_state, first_key, - nettingcontract.address, + nettingcontract.address.decode('hex'), ) first_channel = channel_from_nettingcontract( first_key, @@ -262,7 +262,7 @@ def tester_channels(tester_state, tester_nettingcontracts, reveal_timeout): second_externalstate = ChannelExternalStateTester( tester_state, second_key, - nettingcontract.address, + nettingcontract.address.decode('hex'), ) second_channel = channel_from_nettingcontract( second_key, diff --git a/raiden/tests/integration/test_blockchainservice.py b/raiden/tests/integration/test_blockchainservice.py index 3dbbfd8e7c..198d3cdc05 100644 --- a/raiden/tests/integration/test_blockchainservice.py +++ b/raiden/tests/integration/test_blockchainservice.py @@ -144,7 +144,14 @@ def test_new_netting_contract(raiden_network, token_amount, settle_timeout): assert netting_channel_02.detail(None)['partner_balance'] == 130 # open channel with same peer again after settling - netting_channel_01.close(None) + netting_channel_01.close( + nonce=0, + transferred_amount=0, + locksroot='', + extra_hash='', + signature='', + ) + wait_until_block(app0.raiden.chain, app0.raiden.chain.block_number() + settle_timeout + 1) netting_channel_01.settle() assert netting_channel_01.opened() is '' diff --git a/raiden/tests/integration/test_settlement.py b/raiden/tests/integration/test_settlement.py index 9df79524c5..1a74f72c3d 100644 --- a/raiden/tests/integration/test_settlement.py +++ b/raiden/tests/integration/test_settlement.py @@ -132,7 +132,7 @@ def test_settlement(raiden_network, settle_timeout, reveal_timeout): # a ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle - bob_alice_channel.external_state.netting_channel.close(transfermessage) + bob_alice_channel.external_state.close(transfermessage) wait_until_block(alice_chain, alice_chain.block_number() + 1) assert alice_bob_channel.external_state.close_event.wait(timeout=15) @@ -246,7 +246,7 @@ def test_settled_lock(token_addresses, raiden_network, settle_timeout, reveal_ti last_transfer = get_sent_transfer(forward_channel, 1) # call close giving the secret for a transfer that has being revealed - back_channel.external_state.netting_channel.close(last_transfer) + back_channel.external_state.close(last_transfer) # check that the double unlock will fail with pytest.raises(Exception): diff --git a/raiden/tests/property/smart_contracts/strategies.py b/raiden/tests/property/smart_contracts/strategies.py index 4085eeda3f..19702f2600 100644 --- a/raiden/tests/property/smart_contracts/strategies.py +++ b/raiden/tests/property/smart_contracts/strategies.py @@ -16,11 +16,12 @@ @composite -def direct_transfer(draw, token, recipient, locksroot): +def direct_transfer(draw, token, channel, recipient, locksroot): return DirectTransfer( draw(identifier), draw(nonce), draw(token), + draw(channel), draw(transferred_amount), draw(recipient), draw(locksroot), diff --git a/raiden/tests/property/smart_contracts/test_nettingchannel.py b/raiden/tests/property/smart_contracts/test_nettingchannel.py index 7d0156eadc..df5d4cb9c9 100644 --- a/raiden/tests/property/smart_contracts/test_nettingchannel.py +++ b/raiden/tests/property/smart_contracts/test_nettingchannel.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- +import contextlib + from coincurve import PrivateKey from ethereum.tester import TransactionFailed +from ethereum.processblock import BlockGasLimitReached +from hypothesis import assume from hypothesis.stateful import GenericStateMachine from hypothesis.strategies import ( integers, @@ -22,7 +26,11 @@ direct_transfer, ) from raiden.tests.fixtures.tester import tester_state -from raiden.utils import privatekey_to_address, sha3 +from raiden.utils import ( + make_address, + privatekey_to_address, + sha3, +) DEPOSIT = 'deposit' CLOSE = 'close' @@ -30,6 +38,16 @@ MINE = 'mine' +@contextlib.contextmanager +def transaction_must_fail(error_message): + try: + yield + except TransactionFailed: + pass + else: + raise ValueError(error_message) + + class NettingChannelStateMachine(GenericStateMachine): """ Generates random operations (e.g. deposit, close, updateTransfer) to test against a netting channel. @@ -112,15 +130,22 @@ def __init__(self): sender=self.private_keys[0], ) + self.closing_address = None self.update_transfer_called = False self.participant_addresses = { address_and_balance[0].decode('hex'), address_and_balance[2].decode('hex'), } + self.channel_addresses = [ + self.netting_channel.address.decode('hex'), + make_address(), # used to test invalid transfers + ] + def steps(self): transfer = direct_transfer( # pylint: disable=no-value-for-parameter sampled_from(self.token_addresses), + sampled_from(self.channel_addresses), sampled_from(self.addresses), just(''), ) @@ -145,15 +170,21 @@ def steps(self): sampled_from(self.private_keys), ) + transaction_ops = one_of( + deposit_op, + close_op, + update_transfer_op, + ) + mine_op = tuples( just(MINE), - integers(min_value=1, max_value=100), + integers(min_value=1, max_value=self.settle_timeout * 10), ) + # increases likely hood of the mine op, while permitting transactions + # to run in the same block return one_of( - deposit_op, - close_op, - update_transfer_op, + transaction_ops, mine_op, ) @@ -161,13 +192,22 @@ def execute_step(self, step): op = step[0] if op == DEPOSIT: - self.contract_deposit(step[1], step[2]) + try: + self.contract_deposit(step[1], step[2]) + except BlockGasLimitReached: + assume(False) elif op == CLOSE: - self.contract_close(step[1], step[2], step[3]) + try: + self.contract_close(step[1], step[2], step[3]) + except BlockGasLimitReached: + assume(False) elif op == UPDATE_TRANSFER: - self.contract_update_transfer(step[1], step[2], step[3]) + try: + self.contract_update_transfer(step[1], step[2], step[3]) + except BlockGasLimitReached: + assume(False) elif op == MINE: self.tester_state.mine(number_of_blocks=step[1]) @@ -184,37 +224,25 @@ def contract_deposit(self, deposit_amount, sender_pkey): ) if not self.is_participant(sender_address): - try: + with transaction_must_fail('deposit from non-participant didnt fail'): self.netting_channel.deposit( # pylint: disable=no-member deposit_amount, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('deposit from non-participant didnt fail') elif self.netting_channel.closed(sender=sender_pkey) != 0: # pylint: disable=no-member - try: + with transaction_must_fail('deposit with closed channel didnt fail'): self.netting_channel.deposit( # pylint: disable=no-member deposit_amount, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('deposit with closed channel didnt fail') elif token_balance < deposit_amount: - try: + with transaction_must_fail('having insufficient funds for a deposit didnt fail'): self.netting_channel.deposit( # pylint: disable=no-member deposit_amount, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('having insufficient funds for a deposit didnt fail') else: self.netting_channel.deposit( # pylint: disable=no-member @@ -231,71 +259,77 @@ def contract_close(self, transfer, signing_pkey, sender_pkey): sender_address = privatekey_to_address(sender_pkey) transfer_data = transfer.encode() - opened_block = self.netting_channel.opened(sender=sender_pkey) # pylint: disable=no-member - nonce_start = opened_block * (2 ** 32) - nonce_end = (opened_block + 1) * (2 ** 32) + transfer_hash = sha3(transfer_data[:-65]) if not self.is_participant(transfer.sender): - try: + msg = 'close with transfer data from a non participant didnt fail' + with transaction_must_fail(msg): self.netting_channel.close( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('close with transfer data from a non participant didnt fail') elif transfer.sender == sender_address: - try: + with transaction_must_fail('close with self signed transfer didnt fail'): self.netting_channel.close( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('close with self signed transfer didnt fail') elif self.netting_channel.closed(sender=sender_pkey) != 0: # pylint: disable=no-member - try: + with transaction_must_fail('close called twice didnt fail'): self.netting_channel.close( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('close called twice didnt fail') elif not self.is_participant(sender_address): - try: + with transaction_must_fail('close called by a non participant didnt fail'): self.netting_channel.close( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('close called by a non participant didnt fail') - elif not nonce_start <= transfer.nonce < nonce_end: - try: + elif transfer.channel != self.netting_channel.address.decode('hex'): + msg = 'close called with a transfer for a different channe didnt fail' + with transaction_must_fail(msg): self.netting_channel.close( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('message with invalid nonce didnt fail') else: self.netting_channel.close( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) + self.closing_address = sender_address + def contract_update_transfer(self, transfer, signing_pkey, sender_pkey): transfer.sign( PrivateKey(signing_pkey), @@ -303,74 +337,114 @@ def contract_update_transfer(self, transfer, signing_pkey, sender_pkey): ) sender_address = privatekey_to_address(sender_pkey) + transfer_data = transfer.encode() + transfer_hash = sha3(transfer_data[:-65]) - opened_block = self.netting_channel.opened(sender=sender_pkey) # pylint: disable=no-member - nonce_start = opened_block * (2 ** 32) - nonce_end = (opened_block + 1) * (2 ** 32) + close_block = self.netting_channel.closed(sender=sender_pkey) # pylint: disable=no-member + settlement_end = close_block + self.settle_timeout - transfer_data = transfer.encode() + is_closed = close_block != 0 + is_settlement_period_over = is_closed and settlement_end < self.tester_state.block.number if not self.is_participant(transfer.sender): - try: + msg = 'updateTransfer with transfer data from a non participant didnt fail' + with transaction_must_fail(msg): self.netting_channel.updateTransfer( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError( - 'updateTransfer with transfer data from a non participant didnt fail' - ) elif transfer.sender == sender_address: - try: + with transaction_must_fail('updateTransfer with self signed transfer didnt fail'): self.netting_channel.updateTransfer( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('updateTransfer with self signed transfer didnt fail') elif self.update_transfer_called: - try: + with transaction_must_fail('updateTransfer called twice didnt fail'): self.netting_channel.updateTransfer( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('updateTransfer called twice didnt fail') elif not self.is_participant(sender_address): - try: + with transaction_must_fail('updateTransfer called by a non participant didnt fail'): self.netting_channel.updateTransfer( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('updateTransfer called by a non participant didnt fail') - elif not nonce_start <= transfer.nonce < nonce_end: - try: - self.netting_channel.close( # pylint: disable=no-member - transfer_data, + elif transfer.channel != self.netting_channel.address.decode('hex'): + msg = 'updateTransfer called with a transfer for a different channel didnt fail' + with transaction_must_fail(msg): + self.netting_channel.updateTransfer( # pylint: disable=no-member + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, + sender=sender_pkey, + ) + + elif not is_closed: + with transaction_must_fail('updateTransfer called on an open channel and didnt fail'): + self.netting_channel.updateTransfer( # pylint: disable=no-member + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, + sender=sender_pkey, + ) + + elif is_settlement_period_over: + msg = 'updateTransfer called after end of the settlement period and didnt fail' + with transaction_must_fail(msg): + self.netting_channel.updateTransfer( # pylint: disable=no-member + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, + sender=sender_pkey, + ) + + elif sender_address == self.closing_address: + with transaction_must_fail('updateTransfer called by the closer and it didnt fail'): + self.netting_channel.updateTransfer( # pylint: disable=no-member + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) - except TransactionFailed: - pass - else: - raise ValueError('message with invalid nonce didnt fail') else: self.netting_channel.updateTransfer( # pylint: disable=no-member - transfer_data, + transfer.nonce, + transfer.transferred_amount, + transfer.locksroot, + transfer_hash, + transfer.signature, sender=sender_pkey, ) self.update_transfer_called = True diff --git a/raiden/tests/smart_contracts/netting_channel/AuxiliaryTester.sol b/raiden/tests/smart_contracts/netting_channel/AuxiliaryTester.sol index 511d6c0a38..dc029394ed 100644 --- a/raiden/tests/smart_contracts/netting_channel/AuxiliaryTester.sol +++ b/raiden/tests/smart_contracts/netting_channel/AuxiliaryTester.sol @@ -21,4 +21,20 @@ contract AuxiliaryTester { function signatureSplit(bytes signature) returns (bytes32 r, bytes32 s, uint8 v) { return NettingChannelLibrary.signatureSplit(signature); } + + function recoverAddressFromSignature( + uint64 nonce, + uint256 transferred_amount, + bytes32 locksroot, + bytes32 extra_hash, + bytes signature + ) returns (address) { + return NettingChannelLibrary.recoverAddressFromSignature( + nonce, + transferred_amount, + locksroot, + extra_hash, + signature + ); + } } diff --git a/raiden/tests/smart_contracts/netting_channel/DecoderTester.sol b/raiden/tests/smart_contracts/netting_channel/DecoderTester.sol deleted file mode 100644 index 5b08981d89..0000000000 --- a/raiden/tests/smart_contracts/netting_channel/DecoderTester.sol +++ /dev/null @@ -1,17 +0,0 @@ -pragma solidity ^0.4.0; - -import "raiden/NettingChannelLibrary.sol"; - -contract DecoderTester { - function decodeLock(bytes lock) returns (uint64 expiration, uint amount, bytes32 hashlock) { - return NettingChannelLibrary.decodeLock(lock); - } - - function getTransferRawAddress(bytes signed_transfer) returns (bytes transfer_raw, address signing_address) { - return NettingChannelLibrary.getTransferRawAddress(signed_transfer); - } - - function decodeTransfer(bytes transfer_raw) returns (uint64 nonce, address recipient, bytes32 locksroot, uint256 transferred_amount) { - return NettingChannelLibrary.decodeTransfer(transfer_raw); - } -} diff --git a/raiden/tests/smart_contracts/netting_channel/test_auxiliary_netting_channel.py b/raiden/tests/smart_contracts/netting_channel/test_auxiliary_netting_channel.py index 07ec0b3941..5c2790b405 100644 --- a/raiden/tests/smart_contracts/netting_channel/test_auxiliary_netting_channel.py +++ b/raiden/tests/smart_contracts/netting_channel/test_auxiliary_netting_channel.py @@ -163,6 +163,7 @@ def test_signature_split(tester_state, tester_nettingchannel_library_address): identifier=1, nonce=1, token='x' * 20, + channel=auxiliary.address, transferred_amount=10, recipient='y' * 20, locksroot=HASH @@ -185,3 +186,32 @@ def test_signature_split(tester_state, tester_nettingchannel_library_address): with pytest.raises(TransactionFailed): signature = signature[:-1] + chr(4) r, s, v = auxiliary.signatureSplit(signature) + + +def test_recoverAddressFromSignature(tester_state, tester_nettingchannel_library_address): + auxiliary = deploy_auxiliary_tester(tester_state, tester_nettingchannel_library_address) + privkey, address = make_privkey_address() + + msg = DirectTransfer( + identifier=1, + nonce=1, + token='x' * 20, + channel=auxiliary.address, + transferred_amount=10, + recipient='y' * 20, + locksroot=HASH + ) + msg.sign(privkey, address) + data = msg.encode() + signature = data[-65:] + extra_hash = sha3(data[:-65]) + + computed_address = auxiliary.recoverAddressFromSignature( + msg.nonce, + msg.transferred_amount, + msg.locksroot, + extra_hash, + signature + ) + + assert computed_address.decode('hex') == msg.sender diff --git a/raiden/tests/smart_contracts/netting_channel/test_close.py b/raiden/tests/smart_contracts/netting_channel/test_close.py index f486a4800d..d387c3bae0 100644 --- a/raiden/tests/smart_contracts/netting_channel/test_close.py +++ b/raiden/tests/smart_contracts/netting_channel/test_close.py @@ -10,7 +10,7 @@ make_direct_transfer, ) from raiden.tests.utils.transfer import make_direct_transfer_from_channel -from raiden.utils import privatekey_to_address +from raiden.utils import privatekey_to_address, sha3, make_address def test_close_event(tester_state, tester_nettingcontracts, tester_events): @@ -19,7 +19,7 @@ def test_close_event(tester_state, tester_nettingcontracts, tester_events): address = privatekey_to_address(pkey0) previous_events = list(tester_events) - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) assert len(previous_events) + 1 == len(tester_events) block_number = tester_state.block.number @@ -37,7 +37,7 @@ def test_close_first_participant_can_close(tester_state, tester_nettingcontracts address0 = privatekey_to_address(pkey0) block_number = tester_state.block.number - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) assert nettingchannel.closed(sender=pkey0) == block_number assert nettingchannel.closingAddress(sender=pkey0) == encode_hex(address0) @@ -49,7 +49,7 @@ def test_close_second_participant_can_close(tester_state, tester_nettingcontract address1 = privatekey_to_address(pkey1) closed_block_number = tester_state.block.number - nettingchannel.close('', sender=pkey1) + nettingchannel.close(sender=pkey1) assert nettingchannel.closed(sender=pkey1) == closed_block_number assert nettingchannel.closingAddress(sender=pkey1) == encode_hex(address1) @@ -62,7 +62,7 @@ def test_close_only_participant_can_close(tester_nettingcontracts): nonparticipant_key = tester.k3 with pytest.raises(TransactionFailed): - nettingchannel.close('', sender=nonparticipant_key) + nettingchannel.close(sender=nonparticipant_key) def test_close_first_argument_is_for_partner_transfer(tester_state, tester_channels): @@ -77,15 +77,22 @@ def test_close_first_argument_is_for_partner_transfer(tester_state, tester_chann amount=90, pkey=pkey0, ) - transfer0_data = str(transfer0.packed().data) + transfer0_hash = sha3(transfer0.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.close(transfer0_data, sender=pkey0) + nettingchannel.close( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey0, + ) @pytest.mark.parametrize('number_of_nodes', [3]) def test_close_accepts_only_transfer_from_participants(tester_channels, private_keys): - """ Close must not accept a transfer from a non participant. """ + """ Close must not accept a transfer signed by a non participant. """ pkey0, _, nettingchannel, channel0, _ = tester_channels[0] nonparticipant_key = private_keys[2] opened_block = nettingchannel.opened(sender=pkey0) @@ -95,6 +102,7 @@ def test_close_accepts_only_transfer_from_participants(tester_channels, private_ identifier=1, nonce=1 + (opened_block * (2 ** 32)), token=channel0.token_address, + channel=channel0.channel_address, transferred_amount=10, recipient=channel0.our_address, locksroot='', @@ -104,34 +112,49 @@ def test_close_accepts_only_transfer_from_participants(tester_channels, private_ nonparticipant_sign_key = PrivateKey(nonparticipant_key) transfer_nonparticipant.sign(nonparticipant_sign_key, nonparticipant_address) - transfer_nonparticipant_data = str(transfer_nonparticipant.packed().data) + transfer_nonparticipant_hash = sha3(transfer_nonparticipant.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.close(transfer_nonparticipant_data, sender=pkey0) - - -@pytest.mark.parametrize('number_of_nodes', [3]) -def test_close_wrong_recipient(tester_channels, private_keys): - """ Close must not accept a transfer aimed at a non recipient. """ + nettingchannel.close( + transfer_nonparticipant.nonce, + transfer_nonparticipant.transferred_amount, + transfer_nonparticipant.locksroot, + transfer_nonparticipant_hash, + transfer_nonparticipant.signature, + sender=pkey0, + ) + + +@pytest.mark.parametrize('number_of_nodes', [2]) +def test_close_wrong_channel(tester_channels): + """ Close must not accept a transfer aimed at a different channel. """ pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] opened_block = nettingchannel.opened(sender=pkey0) - nonparticipant_address = privatekey_to_address(private_keys[2]) + wrong_address = make_address() # make a transfer where the recipient is totally wrong - transfer_wrong_recipient = DirectTransfer( + transfer_wrong_channel = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2 ** 32)), token=channel0.token_address, + channel=wrong_address, transferred_amount=10, - recipient=nonparticipant_address, + recipient=channel0.our_address, locksroot='', ) - transfer_wrong_recipient.sign(PrivateKey(pkey1), privatekey_to_address(pkey1)) - transfer_wrong_recipient_data = str(transfer_wrong_recipient.packed().data) + transfer_wrong_channel.sign(PrivateKey(pkey1), privatekey_to_address(pkey1)) + transfer_wrong_channel_hash = sha3(transfer_wrong_channel.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.close(transfer_wrong_recipient_data, sender=pkey0) + nettingchannel.close( + transfer_wrong_channel.nonce, + transfer_wrong_channel.transferred_amount, + transfer_wrong_channel.locksroot, + transfer_wrong_channel_hash, + transfer_wrong_channel.signature, + sender=pkey0, + ) def test_close_called_multiple_times(tester_state, tester_nettingcontracts): @@ -140,13 +163,13 @@ def test_close_called_multiple_times(tester_state, tester_nettingcontracts): address0 = privatekey_to_address(pkey0) closed_block_number = tester_state.block.number - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) with pytest.raises(TransactionFailed): - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) with pytest.raises(TransactionFailed): - nettingchannel.close('', sender=pkey1) + nettingchannel.close(sender=pkey1) assert nettingchannel.closed(sender=pkey0) == closed_block_number assert nettingchannel.closingAddress(sender=pkey0) == encode_hex(address0) @@ -186,10 +209,16 @@ def test_close_valid_tranfer_different_token( sign_key = PrivateKey(pkey0) direct_transfer_other_token.sign(sign_key, address) - direct_transfer_data = direct_transfer_other_token.encode() - + direct_transfer_other_token_hash = sha3(direct_transfer_other_token.encode()[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.close(direct_transfer_data, sender=pkey1) + nettingchannel.close( + direct_transfer_other_token.nonce, + direct_transfer_other_token.transferred_amount, + direct_transfer_other_token.locksroot, + direct_transfer_other_token_hash, + direct_transfer_other_token.signature, + sender=pkey1, + ) def test_close_tampered_identifier(tester_state, tester_channels): @@ -208,10 +237,17 @@ def test_close_tampered_identifier(tester_state, tester_channels): tampered_transfer = DirectTransfer.decode(transfer0_data) tampered_transfer.identifier += 1 - tampered_transfer_data = tampered_transfer.encode() + tampered_transfer_hash = sha3(tampered_transfer.encode()[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.close(tampered_transfer_data, sender=pkey1) + nettingchannel.close( + tampered_transfer.nonce, + tampered_transfer.transferred_amount, + tampered_transfer.locksroot, + tampered_transfer_hash, + tampered_transfer.signature, + sender=pkey1, + ) def test_close_tampered_nonce(tester_state, tester_channels): @@ -230,7 +266,14 @@ def test_close_tampered_nonce(tester_state, tester_channels): tampered_transfer = DirectTransfer.decode(transfer0_data) tampered_transfer.nonce += 1 - tampered_transfer_data = tampered_transfer.encode() + tampered_transfer_hash = sha3(tampered_transfer.encode()[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.close(tampered_transfer_data, sender=pkey1) + nettingchannel.close( + tampered_transfer.nonce, + tampered_transfer.transferred_amount, + tampered_transfer.locksroot, + tampered_transfer_hash, + tampered_transfer.signature, + sender=pkey1, + ) diff --git a/raiden/tests/smart_contracts/netting_channel/test_decoder_netting_channel.py b/raiden/tests/smart_contracts/netting_channel/test_decoder_netting_channel.py deleted file mode 100644 index 0dc470f8ae..0000000000 --- a/raiden/tests/smart_contracts/netting_channel/test_decoder_netting_channel.py +++ /dev/null @@ -1,244 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -import pytest -from ethereum import tester -from ethereum.tester import TransactionFailed - -from coincurve import PrivateKey -from pyethapp.jsonrpc import address_decoder - -from raiden.encoding import messages -from raiden.encoding.format import compute_slices -from raiden.utils import sha3, privatekey_to_address, get_project_root -from raiden.tests.utils.tests import get_relative_contract -from raiden.tests.utils.messages import ( - make_direct_transfer, - make_mediated_transfer, - make_refund_transfer, -) - -VALID_LOCKSROOT = [ - sha3('WaldemarstrWaldemarstrWaldemarst'), - sha3('SikorkaSikorkaSikorkaSikorkaSiko'), - sha3('MainzMainzMainzMainzMainzMainzMa'), -] - - -def assert_decoder_results(message, decoder): - message_encoded = message.encode() - transfer_raw, signing_address = decoder.getTransferRawAddress(message_encoded) - - assert address_decoder(signing_address) == message.sender - assert transfer_raw == message_encoded[:-65] - - ( - nonce_decoded, - recipient_address_decoded, - locksroot_decoded, - transferred_amount_decoded - ) = decoder.decodeTransfer(transfer_raw) - - assert message.nonce == nonce_decoded - assert message.recipient == address_decoder(recipient_address_decoded) - assert message.transferred_amount == transferred_amount_decoded - assert message.locksroot == locksroot_decoded - - -def deploy_decoder_tester(tester_state, tester_nettingchannel_library_address): - contracts_path = os.path.join(get_project_root(), 'smart_contracts') - raiden_remap = 'raiden={}'.format(contracts_path) - - decoder = tester_state.abi_contract( - None, - path=get_relative_contract(__file__, 'DecoderTester.sol'), - language='solidity', - libraries={'NettingChannelLibrary': tester_nettingchannel_library_address.encode('hex')}, - extra_args=raiden_remap, - ) - tester_state.mine(number_of_blocks=1) - - return decoder - - -@pytest.mark.parametrize('identifier', [0, 2 ** 64 - 1]) -@pytest.mark.parametrize('nonce', [1, 2 ** 64 - 1]) -@pytest.mark.parametrize('transferred_amount', [0, 2 ** 256 - 1]) -@pytest.mark.parametrize('locksroot', VALID_LOCKSROOT) -def test_decode_direct_transfer( - identifier, - nonce, - transferred_amount, - locksroot, - tester_state, - tester_nettingchannel_library_address): - - privatekey0 = tester.DEFAULT_KEY - address0 = privatekey_to_address(privatekey0) - - decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address) - - direct_transfer = make_direct_transfer( - identifier=identifier, - nonce=nonce, - transferred_amount=transferred_amount, - locksroot=locksroot, - ) - direct_transfer.sign(PrivateKey(privatekey0), address0) - - assert_decoder_results(direct_transfer, decoder) - - -@pytest.mark.parametrize('amount', [0, 2 ** 256 - 1]) -@pytest.mark.parametrize('identifier', [0, 2 ** 64 - 1]) -@pytest.mark.parametrize('nonce', [1, 2 ** 64 - 1]) -@pytest.mark.parametrize('transferred_amount', [0, 2 ** 256 - 1]) -@pytest.mark.parametrize('fee', [0, 2 ** 256 - 1]) -@pytest.mark.parametrize('locksroot', VALID_LOCKSROOT) -def test_decode_mediated_transfer( - amount, - identifier, - nonce, - transferred_amount, - locksroot, - fee, - tester_state, - tester_nettingchannel_library_address): - - privatekey0 = tester.DEFAULT_KEY - address0 = privatekey_to_address(privatekey0) - - decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address) - - mediated_transfer = make_mediated_transfer( - amount=amount, - identifier=identifier, - nonce=nonce, - fee=fee, - transferred_amount=transferred_amount, - locksroot=locksroot, - ) - - mediated_transfer.sign(PrivateKey(privatekey0), address0) - assert_decoder_results(mediated_transfer, decoder) - - -@pytest.mark.parametrize('amount', [0, 2 ** 256 - 1]) -@pytest.mark.parametrize('identifier', [0, 2 ** 64 - 1]) -@pytest.mark.parametrize('nonce', [1, 2 ** 64 - 1]) -@pytest.mark.parametrize('transferred_amount', [0, 2 ** 256 - 1]) -@pytest.mark.parametrize('locksroot', VALID_LOCKSROOT) -def test_decode_refund_transfer( - amount, - identifier, - nonce, - transferred_amount, - locksroot, - tester_state, - tester_nettingchannel_library_address): - - privatekey0 = tester.DEFAULT_KEY - address0 = privatekey_to_address(privatekey0) - - decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address) - - refund_transfer = make_refund_transfer( - amount=amount, - identifier=identifier, - nonce=nonce, - transferred_amount=transferred_amount, - locksroot=locksroot, - ) - refund_transfer.sign(PrivateKey(privatekey0), address0) - assert_decoder_results(refund_transfer, decoder) - - -def test_decode_tampered_direct_transfer(tester_state, tester_nettingchannel_library_address): - privatekey0 = tester.DEFAULT_KEY - address0 = privatekey_to_address(privatekey0) - - decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address) - - direct_transfer = make_direct_transfer() - direct_transfer.sign(PrivateKey(privatekey0), address0) - - message_encoded = direct_transfer.encode() - transfer_raw, _ = decoder.getTransferRawAddress(message_encoded) - - names_slices = compute_slices(messages.DirectTransfer.fields_spec) - for name, slice_ in names_slices.iteritems(): - if name == 'signature': - continue - - tampered_transfer = bytearray(transfer_raw) - tampered_transfer.pop(slice_.start) - tampered_transfer = str(tampered_transfer) - with pytest.raises(TransactionFailed): - decoder.decodeTransfer(tampered_transfer) - - tampered_transfer = bytearray(transfer_raw) - tampered_transfer.pop(slice_.stop - 1) - tampered_transfer = str(tampered_transfer) - with pytest.raises(TransactionFailed): - decoder.decodeTransfer(tampered_transfer) - - -def test_decode_tampered_mediated_transfer(tester_state, tester_nettingchannel_library_address): - privatekey0 = tester.DEFAULT_KEY - address0 = privatekey_to_address(privatekey0) - - decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address) - - mediated_transfer = make_mediated_transfer() - - mediated_transfer.sign(PrivateKey(privatekey0), address0) - - message_encoded = mediated_transfer.encode() - transfer_raw, _ = decoder.getTransferRawAddress(message_encoded) - - names_slices = compute_slices(messages.MediatedTransfer.fields_spec) - for name, slice_ in names_slices.iteritems(): - if name == 'signature': - continue - - tampered_transfer = bytearray(transfer_raw) - tampered_transfer.pop(slice_.start) - tampered_transfer = str(tampered_transfer) - with pytest.raises(TransactionFailed): - decoder.decodeTransfer(tampered_transfer) - - tampered_transfer = bytearray(transfer_raw) - tampered_transfer.pop(slice_.stop - 1) - tampered_transfer = str(tampered_transfer) - with pytest.raises(TransactionFailed): - decoder.decodeTransfer(tampered_transfer) - - -def test_decode_tampered_refund_transfer(tester_state, tester_nettingchannel_library_address): - privatekey0 = tester.DEFAULT_KEY - address0 = privatekey_to_address(privatekey0) - - decoder = deploy_decoder_tester(tester_state, tester_nettingchannel_library_address) - - refund_transfer = make_refund_transfer() - refund_transfer.sign(PrivateKey(privatekey0), address0) - - message_encoded = refund_transfer.encode() - transfer_raw, _ = decoder.getTransferRawAddress(message_encoded) - - names_slices = compute_slices(messages.RefundTransfer.fields_spec) - for name, slice_ in names_slices.iteritems(): - if name == 'signature': - continue - - tampered_transfer = bytearray(transfer_raw) - tampered_transfer.pop(slice_.start) - tampered_transfer = str(tampered_transfer) - with pytest.raises(TransactionFailed): - decoder.decodeTransfer(tampered_transfer) - - tampered_transfer = bytearray(transfer_raw) - tampered_transfer.pop(slice_.stop - 1) - tampered_transfer = str(tampered_transfer) - with pytest.raises(TransactionFailed): - decoder.decodeTransfer(tampered_transfer) diff --git a/raiden/tests/smart_contracts/netting_channel/test_settle.py b/raiden/tests/smart_contracts/netting_channel/test_settle.py index 7f4124acd0..93daab193a 100644 --- a/raiden/tests/smart_contracts/netting_channel/test_settle.py +++ b/raiden/tests/smart_contracts/netting_channel/test_settle.py @@ -15,7 +15,7 @@ def test_settle_event(settle_timeout, tester_state, tester_events, tester_nettin """ The event ChannelSettled is emitted when the channel is settled. """ pkey0, _, nettingchannel = tester_nettingcontracts[0] - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) tester_state.mine(number_of_blocks=settle_timeout + 1) @@ -49,7 +49,7 @@ def test_settle_unused_channel( initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -87,9 +87,16 @@ def test_settle_single_direct_transfer_for_closing_party( amount, pkey0, ) - transfer0_data = str(transfer0.packed().data) - - nettingchannel.close(transfer0_data, sender=pkey1) + transfer0_hash = sha3(transfer0.packed().data[:-65]) + + nettingchannel.close( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -127,10 +134,17 @@ def test_settle_single_direct_transfer_for_counterparty( amount, pkey0, ) - transfer0_data = str(transfer0.packed().data) - - nettingchannel.close('', sender=pkey0) - nettingchannel.updateTransfer(transfer0_data, sender=pkey1) + transfer0_hash = sha3(transfer0.packed().data[:-65]) + + nettingchannel.close(sender=pkey0) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -166,7 +180,6 @@ def test_settle_two_direct_transfers( amount0, pkey0, ) - transfer0_data = str(transfer0.packed().data) amount1 = 30 block_number = tester_state.block.number @@ -177,10 +190,26 @@ def test_settle_two_direct_transfers( amount1, pkey1, ) - transfer1_data = str(transfer1.packed().data) + transfer1_hash = sha3(transfer1.packed().data[:-65]) + + nettingchannel.close( + transfer1.nonce, + transfer1.transferred_amount, + transfer1.locksroot, + transfer1_hash, + transfer1.signature, + sender=pkey0, + ) - nettingchannel.close(transfer1_data, sender=pkey0) - nettingchannel.updateTransfer(transfer0_data, sender=pkey1) + transfer0_hash = sha3(transfer0.packed().data[:-65]) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -219,7 +248,7 @@ def test_settle_with_locked_mediated_transfer_for_counterparty( channel0.state_transition(new_block) channel1.state_transition(new_block) lock0 = Lock(amount=29, expiration=expiration0, hashlock=sha3('lock1')) - mediated = make_mediated_transfer( + mediated0 = make_mediated_transfer( channel0, channel1, address0, @@ -229,10 +258,17 @@ def test_settle_with_locked_mediated_transfer_for_counterparty( tester_state.block.number, ) - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) - transfer_data = str(mediated.packed().data) - nettingchannel.updateTransfer(transfer_data, sender=pkey1) + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.updateTransfer( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey1) @@ -273,7 +309,7 @@ def test_settle_with_locked_mediated_transfer_for_closing_party( channel0.state_transition(new_block) channel1.state_transition(new_block) lock0 = Lock(amount=29, expiration=expiration0, hashlock=sha3('lock1')) - mediated = make_mediated_transfer( + mediated0 = make_mediated_transfer( channel0, channel1, address0, @@ -283,8 +319,15 @@ def test_settle_with_locked_mediated_transfer_for_closing_party( tester_state.block.number, ) - transfer_data = str(mediated.packed().data) - nettingchannel.close(transfer_data, sender=pkey1) + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.close( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey1) @@ -333,7 +376,6 @@ def test_settle_two_locked_mediated_transfer_messages( pkey0, tester_state.block.number, ) - mediated0_data = str(mediated0.packed().data) lock_expiration1 = tester_state.block.number + reveal_timeout + 5 lock1 = Lock(amount=31, expiration=lock_expiration1, hashlock=sha3('lock2')) @@ -346,10 +388,26 @@ def test_settle_two_locked_mediated_transfer_messages( pkey1, tester_state.block.number, ) - mediated1_data = str(mediated1.packed().data) - nettingchannel.close(mediated0_data, sender=pkey1) - nettingchannel.updateTransfer(mediated1_data, sender=pkey0) + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.close( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey1, + ) + + mediated1_hash = sha3(mediated1.packed().data[:-65]) + nettingchannel.updateTransfer( + mediated1.nonce, + mediated1.transferred_amount, + mediated1.locksroot, + mediated1_hash, + mediated1.signature, + sender=pkey0, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -399,10 +457,18 @@ def test_two_direct_transfers( second_amount0, pkey0, ) - second_direct0_data = str(second_direct0.packed().data) - nettingchannel.close('', sender=pkey0) - nettingchannel.updateTransfer(second_direct0_data, sender=pkey1) + nettingchannel.close(sender=pkey0) + + second_direct0_hash = sha3(second_direct0.packed().data[:-65]) + nettingchannel.updateTransfer( + second_direct0.nonce, + second_direct0.transferred_amount, + second_direct0.locksroot, + second_direct0_hash, + second_direct0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -456,10 +522,18 @@ def test_mediated_after_direct_transfer( pkey0, tester_state.block.number, ) - second_mediated0_data = str(second_mediated0.packed().data) - nettingchannel.close('', sender=pkey0) - nettingchannel.updateTransfer(second_mediated0_data, sender=pkey1) + nettingchannel.close(sender=pkey0) + + second_mediated0_hash = sha3(second_mediated0.packed().data[:-65]) + nettingchannel.updateTransfer( + second_mediated0.nonce, + second_mediated0.transferred_amount, + second_mediated0.locksroot, + second_mediated0_hash, + second_mediated0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -497,7 +571,6 @@ def test_settlement_with_unauthorized_token_transfer( amount0, pkey0, ) - transfer0_data = str(transfer0.packed().data) amount1 = 30 block_number = tester_state.block.number @@ -508,13 +581,29 @@ def test_settlement_with_unauthorized_token_transfer( amount1, pkey1, ) - transfer1_data = str(transfer1.packed().data) extra_amount = 10 assert tester_token.transfer(nettingchannel.address, extra_amount, sender=pkey0) - nettingchannel.close(transfer1_data, sender=pkey0) - nettingchannel.updateTransfer(transfer0_data, sender=pkey1) + transfer1_hash = sha3(transfer1.packed().data[:-65]) + nettingchannel.close( + transfer1.nonce, + transfer1.transferred_amount, + transfer1.locksroot, + transfer1_hash, + transfer1.signature, + sender=pkey0, + ) + + transfer0_hash = sha3(transfer0.packed().data[:-65]) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) @@ -554,7 +643,6 @@ def test_netting(deposit, settle_timeout, tester_channels, tester_state, tester_ amount0, pkey0, ) - direct0_data = str(direct0.packed().data) amount1 = 30 transferred_amount1 += amount1 @@ -566,10 +654,26 @@ def test_netting(deposit, settle_timeout, tester_channels, tester_state, tester_ amount1, pkey1, ) - direct1_data = str(direct1.packed().data) - nettingchannel.close(direct1_data, sender=pkey0) - nettingchannel.updateTransfer(direct0_data, sender=pkey1) + direct1_hash = sha3(direct1.packed().data[:-65]) + nettingchannel.close( + direct1.nonce, + direct1.transferred_amount, + direct1.locksroot, + direct1_hash, + direct1.signature, + sender=pkey0, + ) + + direct0_hash = sha3(direct0.packed().data[:-65]) + nettingchannel.updateTransfer( + direct0.nonce, + direct0.transferred_amount, + direct0.locksroot, + direct0_hash, + direct0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) diff --git a/raiden/tests/smart_contracts/netting_channel/test_updatetransfer.py b/raiden/tests/smart_contracts/netting_channel/test_updatetransfer.py index abd24cc3d2..82cfe5da82 100644 --- a/raiden/tests/smart_contracts/netting_channel/test_updatetransfer.py +++ b/raiden/tests/smart_contracts/netting_channel/test_updatetransfer.py @@ -4,7 +4,7 @@ from coincurve import PrivateKey from raiden.messages import DirectTransfer -from raiden.utils import privatekey_to_address +from raiden.utils import privatekey_to_address, sha3, make_address from raiden.tests.utils.transfer import make_direct_transfer_from_channel @@ -24,12 +24,19 @@ def test_transfer_update_event(tester_state, tester_channels, tester_events): amount=90, pkey=pkey0, ) - direct0_data = str(direct0.packed().data) - - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) previous_events = list(tester_events) - nettingchannel.updateTransfer(direct0_data, sender=pkey1) + + direct0_hash = sha3(direct0.packed().data[:-65]) + nettingchannel.updateTransfer( + direct0.nonce, + direct0.transferred_amount, + direct0.locksroot, + direct0_hash, + direct0.signature, + sender=pkey1, + ) assert len(previous_events) + 1 == len(tester_events) assert tester_events[-1] == { @@ -51,10 +58,17 @@ def test_update_fails_on_open_channel(tester_state, tester_channels): amount=10, pkey=pkey0, ) - transfer0_data = str(transfer0.packed().data) + transfer0_hash = sha3(transfer0.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer0_data, sender=pkey0) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey0, + ) def test_update_not_allowed_after_settlement_period(settle_timeout, tester_channels, tester_state): @@ -69,13 +83,20 @@ def test_update_not_allowed_after_settlement_period(settle_timeout, tester_chann amount=70, pkey=pkey0, ) - direct0_data = str(direct0.packed().data) - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) tester_state.mine(number_of_blocks=settle_timeout + 1) + direct0_hash = sha3(direct0.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(direct0_data, sender=pkey1) + nettingchannel.updateTransfer( + direct0.nonce, + direct0.transferred_amount, + direct0.locksroot, + direct0_hash, + direct0.signature, + sender=pkey1, + ) def test_update_not_allowed_for_the_closing_address(tester_state, tester_channels): @@ -90,7 +111,6 @@ def test_update_not_allowed_for_the_closing_address(tester_state, tester_channel amount=10, pkey=pkey0, ) - transfer0_data = str(transfer0.packed().data) block_number = tester_state.block.number transfer1 = make_direct_transfer_from_channel( @@ -100,17 +120,32 @@ def test_update_not_allowed_for_the_closing_address(tester_state, tester_channel amount=10, pkey=pkey1, ) - transfer1_data = str(transfer1.packed().data) - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) # do not accept a transfer from the party that closed + transfer0_hash = sha3(transfer0.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer0_data, sender=pkey0) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey0, + ) # nor a transfer from the partner + transfer1_hash = sha3(transfer1.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer1_data, sender=pkey0) + nettingchannel.updateTransfer( + transfer1.nonce, + transfer1.transferred_amount, + transfer1.locksroot, + transfer1_hash, + transfer1.signature, + sender=pkey0, + ) @pytest.mark.parametrize('number_of_nodes', [3]) @@ -125,6 +160,7 @@ def test_update_must_fail_with_a_nonparticipant_transfer(tester_channels, privat identifier=1, nonce=1 + (opened_block * (2 ** 32)), token=channel0.token_address, + channel=channel0.channel_address, transferred_amount=10, recipient=channel1.our_address, locksroot='', @@ -134,28 +170,36 @@ def test_update_must_fail_with_a_nonparticipant_transfer(tester_channels, privat nonparticipant_sign_key = PrivateKey(nonparticipant_key) transfer_nonparticipant.sign(nonparticipant_sign_key, nonparticipant_address) - transfer_nonparticipant_data = str(transfer_nonparticipant.packed().data) - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) + transfer_nonparticipant_hash = sha3(transfer_nonparticipant.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer_nonparticipant_data, sender=pkey1) + nettingchannel.updateTransfer( + transfer_nonparticipant.nonce, + transfer_nonparticipant.transferred_amount, + transfer_nonparticipant.locksroot, + transfer_nonparticipant_hash, + transfer_nonparticipant.signature, + sender=pkey1, + ) @pytest.mark.parametrize('number_of_nodes', [3]) -def test_update_must_fail_with_a_wrong_recipient(tester_channels, private_keys): - """ updateTransfer must not accept a transfer from a non participant. """ - pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] +def test_update_must_fail_with_a_channel_address(tester_channels, private_keys): + """ updateTransfer must not accept a transfer signed with the wrong channel address. """ + pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] opened_block = nettingchannel.opened(sender=pkey0) - nonparticipant_address = privatekey_to_address(private_keys[2]) + wrong_channel = make_address() # make a transfer where pkey1 is the target transfer_wrong_recipient = DirectTransfer( identifier=1, nonce=1 + (opened_block * (2 ** 32)), token=channel0.token_address, + channel=wrong_channel, transferred_amount=10, - recipient=nonparticipant_address, + recipient=channel1.our_address, locksroot='', ) @@ -163,12 +207,19 @@ def test_update_must_fail_with_a_wrong_recipient(tester_channels, private_keys): our_sign_key = PrivateKey(pkey0) transfer_wrong_recipient.sign(our_sign_key, our_address) - transfer_wrong_recipient_data = str(transfer_wrong_recipient.packed().data) - nettingchannel.close('', sender=pkey0) + nettingchannel.close(sender=pkey0) + transfer_wrong_recipient_hash = sha3(transfer_wrong_recipient.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer_wrong_recipient_data, sender=pkey1) + nettingchannel.updateTransfer( + transfer_wrong_recipient.nonce, + transfer_wrong_recipient.transferred_amount, + transfer_wrong_recipient.locksroot, + transfer_wrong_recipient_hash, + transfer_wrong_recipient.signature, + sender=pkey1, + ) def test_update_called_multiple_times_same_transfer(tester_state, tester_channels): @@ -183,13 +234,28 @@ def test_update_called_multiple_times_same_transfer(tester_state, tester_channel amount=10, pkey=pkey0, ) - transfer0_data = str(transfer0.packed().data) - nettingchannel.close('', sender=pkey0) - nettingchannel.updateTransfer(transfer0_data, sender=pkey1) + nettingchannel.close(sender=pkey0) + + transfer0_hash = sha3(transfer0.packed().data[:-65]) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer0_data, sender=pkey1) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) def test_update_called_multiple_times_new_transfer(tester_state, tester_channels): @@ -204,7 +270,6 @@ def test_update_called_multiple_times_new_transfer(tester_state, tester_channels amount=10, pkey=pkey0, ) - transfer0_data = str(transfer0.packed().data) block_number = tester_state.block.number transfer1 = make_direct_transfer_from_channel( @@ -214,13 +279,29 @@ def test_update_called_multiple_times_new_transfer(tester_state, tester_channels amount=10, pkey=pkey0, ) - transfer1_data = str(transfer1.packed().data) - nettingchannel.close('', sender=pkey0) - nettingchannel.updateTransfer(transfer0_data, sender=pkey1) + nettingchannel.close(sender=pkey0) + + transfer0_hash = sha3(transfer0.packed().data[:-65]) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) + transfer1_hash = sha3(transfer1.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer1_data, sender=pkey1) + nettingchannel.updateTransfer( + transfer1.nonce, + transfer1.transferred_amount, + transfer1.locksroot, + transfer1_hash, + transfer1.signature, + sender=pkey1, + ) def test_update_called_multiple_times_older_transfer(tester_state, tester_channels): @@ -235,7 +316,6 @@ def test_update_called_multiple_times_older_transfer(tester_state, tester_channe amount=10, pkey=pkey0, ) - transfer0_data = str(transfer0.packed().data) block_number = tester_state.block.number transfer1 = make_direct_transfer_from_channel( @@ -245,10 +325,26 @@ def test_update_called_multiple_times_older_transfer(tester_state, tester_channe amount=10, pkey=pkey0, ) - transfer1_data = str(transfer1.packed().data) - nettingchannel.close('', sender=pkey0) - nettingchannel.updateTransfer(transfer1_data, sender=pkey1) + nettingchannel.close(sender=pkey0) + + transfer1_hash = sha3(transfer1.packed().data[:-65]) + nettingchannel.updateTransfer( + transfer1.nonce, + transfer1.transferred_amount, + transfer1.locksroot, + transfer1_hash, + transfer1.signature, + sender=pkey1, + ) + transfer0_hash = sha3(transfer0.packed().data[:-65]) with pytest.raises(TransactionFailed): - nettingchannel.updateTransfer(transfer0_data, sender=pkey1) + nettingchannel.updateTransfer( + transfer0.nonce, + transfer0.transferred_amount, + transfer0.locksroot, + transfer0_hash, + transfer0.signature, + sender=pkey1, + ) diff --git a/raiden/tests/smart_contracts/netting_channel/test_withdraw.py b/raiden/tests/smart_contracts/netting_channel/test_withdraw.py index 0aaa3ce070..08fa5edbb8 100644 --- a/raiden/tests/smart_contracts/netting_channel/test_withdraw.py +++ b/raiden/tests/smart_contracts/netting_channel/test_withdraw.py @@ -51,14 +51,21 @@ def test_withdraw( tester_state.block.number, secret, ) - mediated0_data = str(mediated0.packed().data) proof = channel1.our_state.balance_proof.compute_proof_for_lock( secret, mediated0.lock, ) - nettingchannel.close(mediated0_data, sender=pkey1) + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.close( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=1) @@ -120,6 +127,7 @@ def test_withdraw_at_settlement_block( identifier=1, nonce=nonce, token=tester_token.address, + channel=nettingchannel.address.decode('hex'), transferred_amount=0, recipient=address1, locksroot=lock0_hash, @@ -131,8 +139,16 @@ def test_withdraw_at_settlement_block( sign_key0 = PrivateKey(pkey0) mediated0.sign(sign_key0, address0) - mediated0_data = str(mediated0.packed().data) - nettingchannel.close(mediated0_data, sender=pkey1) + + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.close( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey1, + ) block_until_settlement_end = lock_expiration - tester_state.block.number tester_state.mine(number_of_blocks=block_until_settlement_end) @@ -177,9 +193,16 @@ def test_withdraw_expired_lock(reveal_timeout, tester_channels, tester_state): tester_state.block.number, secret, ) - mediated0_data = str(mediated0.packed().data) - nettingchannel.close(mediated0_data, sender=pkey0) + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.close( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey0, + ) # expire the lock tester_state.mine(number_of_blocks=lock_timeout + 1) @@ -239,7 +262,6 @@ def test_withdraw_both_participants( tester_state.block.number, secret, ) - mediated01_data = str(mediated01.packed().data) mediated10 = make_mediated_transfer( channel1, @@ -251,12 +273,27 @@ def test_withdraw_both_participants( tester_state.block.number, secret, ) - mediated10_data = str(mediated10.packed().data) - nettingchannel.close(mediated01_data, sender=pkey1) + mediated01_hash = sha3(mediated01.packed().data[:-65]) + nettingchannel.close( + mediated01.nonce, + mediated01.transferred_amount, + mediated01.locksroot, + mediated01_hash, + mediated01.signature, + sender=pkey1, + ) tester_state.mine(number_of_blocks=1) - nettingchannel.updateTransfer(mediated10_data, sender=pkey0) + mediated10_hash = sha3(mediated10.packed().data[:-65]) + nettingchannel.updateTransfer( + mediated10.nonce, + mediated10.transferred_amount, + mediated10.locksroot, + mediated10_hash, + mediated10.signature, + sender=pkey0, + ) tester_state.mine(number_of_blocks=1) proof01 = channel1.our_state.balance_proof.compute_proof_for_lock( @@ -312,9 +349,16 @@ def test_withdraw_twice(reveal_timeout, tester_channels, tester_state): tester_state.block.number, secret, ) - mediated0_data = str(mediated0.packed().data) - nettingchannel.close(mediated0_data, sender=pkey0) + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.close( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey0, + ) unlock_proofs = list(channel0.our_state.balance_proof.get_known_unlocks()) proof = unlock_proofs[0] @@ -343,7 +387,7 @@ def test_withdraw_fails_with_partial_merkle_proof( settle_timeout): """ withdraw must fail if informed proof is not complete. """ - pkey0, pkey1, nettingchannel, _, _ = tester_channels[0] + pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] current_block = tester_state.block.number expiration = current_block + settle_timeout - 1 @@ -361,6 +405,7 @@ def test_withdraw_fails_with_partial_merkle_proof( nonce = 1 + (opened_block * (2 ** 32)) direct_transfer = make_direct_transfer( nonce=nonce, + channel=channel0.channel_address, locksroot=merkle_tree.merkleroot, recipient=privatekey_to_address(pkey1) ) @@ -369,8 +414,15 @@ def test_withdraw_fails_with_partial_merkle_proof( sign_key = PrivateKey(pkey0) direct_transfer.sign(sign_key, address) - direct_transfer_data = str(direct_transfer.packed().data) - nettingchannel.close(direct_transfer_data, sender=pkey1) + direct_transfer_hash = sha3(direct_transfer.packed().data[:-65]) + nettingchannel.close( + direct_transfer.nonce, + direct_transfer.transferred_amount, + direct_transfer.locksroot, + direct_transfer_hash, + direct_transfer.signature, + sender=pkey1, + ) for lock in locks: secret = HASHLOCKS_SECRESTS[lock.hashlock] @@ -394,7 +446,7 @@ def test_withdraw_fails_with_partial_merkle_proof( @pytest.mark.parametrize('tree', HASHLOCK_FOR_MERKLETREE) def test_withdraw_tampered_merkle_proof(tree, tester_channels, tester_state, settle_timeout): """ withdraw must fail if the proof is tampered. """ - pkey0, pkey1, nettingchannel, _, _ = tester_channels[0] + pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] current_block = tester_state.block.number expiration = current_block + settle_timeout - 1 @@ -412,6 +464,7 @@ def test_withdraw_tampered_merkle_proof(tree, tester_channels, tester_state, set nonce = 1 + (opened_block * (2 ** 32)) direct_transfer = make_direct_transfer( nonce=nonce, + channel=channel0.channel_address, locksroot=merkle_tree.merkleroot, recipient=privatekey_to_address(pkey1) ) @@ -420,8 +473,15 @@ def test_withdraw_tampered_merkle_proof(tree, tester_channels, tester_state, set sign_key = PrivateKey(pkey0) direct_transfer.sign(sign_key, address) - direct_transfer_data = str(direct_transfer.packed().data) - nettingchannel.close(direct_transfer_data, sender=pkey1) + direct_transfer_hash = sha3(direct_transfer.packed().data[:-65]) + nettingchannel.close( + direct_transfer.nonce, + direct_transfer.transferred_amount, + direct_transfer.locksroot, + direct_transfer_hash, + direct_transfer.signature, + sender=pkey1, + ) for lock in locks: secret = HASHLOCKS_SECRESTS[lock.hashlock] @@ -456,7 +516,7 @@ def test_withdraw_tampered_lock_amount( settle_timeout): """ withdraw must fail if the lock amonut is tampered. """ - pkey0, pkey1, nettingchannel, _, _ = tester_channels[0] + pkey0, pkey1, nettingchannel, channel0, _ = tester_channels[0] current_block = tester_state.block.number expiration = current_block + settle_timeout - 1 @@ -474,6 +534,7 @@ def test_withdraw_tampered_lock_amount( nonce = 1 + (opened_block * (2 ** 32)) direct_transfer = make_direct_transfer( nonce=nonce, + channel=channel0.channel_address, locksroot=merkle_tree.merkleroot, token=tester_token.address, recipient=privatekey_to_address(pkey1) @@ -483,8 +544,15 @@ def test_withdraw_tampered_lock_amount( sign_key = PrivateKey(pkey0) direct_transfer.sign(sign_key, address) - direct_transfer_data = str(direct_transfer.packed().data) - nettingchannel.close(direct_transfer_data, sender=pkey1) + direct_transfer_hash = sha3(direct_transfer.packed().data[:-65]) + nettingchannel.close( + direct_transfer.nonce, + direct_transfer.transferred_amount, + direct_transfer.locksroot, + direct_transfer_hash, + direct_transfer.signature, + sender=pkey1, + ) for lock in locks: secret = HASHLOCKS_SECRESTS[lock.hashlock] @@ -538,7 +606,7 @@ def test_withdraw_lock_with_a_large_expiration( expiration=lock_expiration, hashlock=lock_hashlock, ) - mediated = make_mediated_transfer( + mediated0 = make_mediated_transfer( channel0, channel1, address0, @@ -549,9 +617,17 @@ def test_withdraw_lock_with_a_large_expiration( secret, ) - nettingchannel.close('', sender=pkey0) - transfer_data = str(mediated.packed().data) - nettingchannel.updateTransfer(transfer_data, sender=pkey1) + nettingchannel.close(sender=pkey0) + + mediated0_hash = sha3(mediated0.packed().data[:-65]) + nettingchannel.updateTransfer( + mediated0.nonce, + mediated0.transferred_amount, + mediated0.locksroot, + mediated0_hash, + mediated0.signature, + sender=pkey1, + ) unlock_proofs = list(channel1.our_state.balance_proof.get_known_unlocks()) proof = unlock_proofs[0] diff --git a/raiden/tests/smart_contracts/test_channel_manager.py b/raiden/tests/smart_contracts/test_channel_manager.py index 0de53c3fad..c14adebccf 100644 --- a/raiden/tests/smart_contracts/test_channel_manager.py +++ b/raiden/tests/smart_contracts/test_channel_manager.py @@ -11,7 +11,7 @@ def netting_channel_settled(tester_state, nettingchannel, pkey, settle_timeout): - nettingchannel.close('', sender=pkey) + nettingchannel.close(sender=pkey) tester_state.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey) tester_state.mine(1) diff --git a/raiden/tests/unit/test_channel.py b/raiden/tests/unit/test_channel.py index 79507dee7a..fc79ed0767 100644 --- a/raiden/tests/unit/test_channel.py +++ b/raiden/tests/unit/test_channel.py @@ -22,7 +22,7 @@ class NettingChannelMock(object): # pylint: disable=no-self-use def __init__(self): - self.address = 'channeladdresschanneladdresschanneladdre' + self.address = 'channeladdresschanne' def opened(self): return 1 @@ -51,6 +51,7 @@ def test_end_state(): token_address = make_address() privkey1, address1 = make_privkey_address() address2 = make_address() + channel_address = make_address() balance1 = 70 balance2 = 110 @@ -91,9 +92,10 @@ def test_end_state(): locksroot = state2.compute_merkleroot_with(lock) locked_transfer = LockedTransfer( - 1, # TODO: fill in identifier + 1, nonce=state1.nonce, token=token_address, + channel=channel_address, transferred_amount=transferred_amount, recipient=state2.address, locksroot=locksroot, @@ -685,6 +687,7 @@ def test_register_invalid_transfer(raiden_network, settle_timeout): 1, # TODO: fill in identifier nonce=channel0.our_state.nonce, token=channel0.token_address, + channel=channel0.channel_address, transferred_amount=channel1.balance + balance0 + amount, recipient=channel0.partner_state.address, locksroot=channel0.partner_state.balance_proof.merkleroot_for_unclaimed(), @@ -786,7 +789,7 @@ def closed(self): def settled(self): return 0 - def close(self, transfer): + def close(self, nonce, transferred_amount, locksroot, extra_hash, signature): self.close_calls += 1 netting_channel = NettingChannelMock() diff --git a/raiden/tests/unit/test_transfer.py b/raiden/tests/unit/test_transfer.py index 8b32278b82..735fbf7e39 100644 --- a/raiden/tests/unit/test_transfer.py +++ b/raiden/tests/unit/test_transfer.py @@ -425,6 +425,7 @@ def test_receive_directtransfer_unknown(raiden_network): identifier=1, nonce=1, token=graph0.token_address, + channel=other_address, transferred_amount=10, recipient=app0.raiden.address, locksroot=HASH @@ -447,6 +448,7 @@ def test_receive_mediatedtransfer_unknown(raiden_network): identifier=1, nonce=1, token=graph0.token_address, + channel=other_address, transferred_amount=amount, recipient=app0.raiden.address, locksroot=locksroot, @@ -473,6 +475,7 @@ def test_receive_hashlocktransfer_unknown(raiden_network): identifier=1, nonce=1, token=graph0.token_address, + channel=other_address, transferred_amount=amount, recipient=app0.raiden.address, locksroot=HASH, @@ -532,6 +535,7 @@ def test_receive_directtransfer_outoforder(raiden_network, private_keys): identifier=identifier, nonce=1, token=graph0.token_address, + channel=channel0.channel_address, transferred_amount=10, recipient=app1.raiden.address, locksroot=HASH, @@ -545,11 +549,15 @@ def test_receive_directtransfer_outoforder(raiden_network, private_keys): @pytest.mark.parametrize('channels_per_node', [2]) def test_receive_mediatedtransfer_outoforder(raiden_network, private_keys): alice_app = raiden_network[0] + bob_app = raiden_network[1] + messages = setup_messages_cb() graph = alice_app.raiden.token_to_channelgraph.values()[0] token_address = graph.token_address + channel0 = channel(alice_app, bob_app, token_address) + mt_helper = MediatedTransferTestHelper(raiden_network, graph) initiator_address = alice_app.raiden.address path = mt_helper.get_paths_of_length(initiator_address, 2) @@ -577,6 +585,7 @@ def test_receive_mediatedtransfer_outoforder(raiden_network, private_keys): identifier=identifier, nonce=1, token=token_address, + channel=channel0.channel_address, transferred_amount=amount, recipient=bob_address, locksroot=locksroot, @@ -595,10 +604,13 @@ def test_receive_mediatedtransfer_outoforder(raiden_network, private_keys): @pytest.mark.parametrize('channels_per_node', [2]) def test_receive_mediatedtransfer_invalid_address(raiden_network, private_keys): alice_app = raiden_network[0] + bob_app = raiden_network[1] graph = alice_app.raiden.token_to_channelgraph.values()[0] token_address = graph.token_address + channel0 = channel(alice_app, bob_app, token_address) + mt_helper = MediatedTransferTestHelper(raiden_network, graph) initiator_address = alice_app.raiden.address path = mt_helper.get_paths_of_length(initiator_address, 2) @@ -623,6 +635,7 @@ def test_receive_mediatedtransfer_invalid_address(raiden_network, private_keys): identifier=identifier, nonce=1, token=token_address, + channel=channel0.channel_address, transferred_amount=amount, recipient=bob_address, locksroot=locksroot, @@ -679,6 +692,7 @@ def test_receive_directtransfer_wrongtoken(raiden_network, private_keys): identifier=identifier, nonce=2, token=HASH[0:20], + channel=channel0.channel_address, transferred_amount=10, recipient=app1.raiden.address, locksroot=HASH, @@ -726,6 +740,7 @@ def test_receive_directtransfer_invalidlocksroot(raiden_network, private_keys): identifier=identifier, nonce=2, token=graph0.token_address, + channel=channel0.channel_address, transferred_amount=10, recipient=app1.raiden.address, locksroot=HASH, @@ -767,7 +782,7 @@ def test_transfer_from_outdated(raiden_network, settle_timeout): channel1, balance1 + amount, [] ) - channel1.external_state.netting_channel.close( + channel1.external_state.close( channel1.received_transfers[-1], ) @@ -798,6 +813,7 @@ def test_transfer_from_outdated(raiden_network, settle_timeout): identifier=1, nonce=1, token=graph0.token_address, + channel=channel0.channel_address, transferred_amount=10, recipient=app0.raiden.address, locksroot=HASH diff --git a/raiden/tests/utils/messages.py b/raiden/tests/utils/messages.py index 4b0c349d35..0d3da22caa 100644 --- a/raiden/tests/utils/messages.py +++ b/raiden/tests/utils/messages.py @@ -81,6 +81,7 @@ def make_refund_transfer( identifier=0, nonce=1, token=ADDRESS, + channel=ADDRESS, transferred_amount=0, amount=1, locksroot='', @@ -94,6 +95,7 @@ def make_refund_transfer( identifier, nonce, token, + channel, transferred_amount, recipient, locksroot, @@ -108,6 +110,7 @@ def make_mediated_transfer( identifier=0, nonce=1, token=ADDRESS, + channel=ADDRESS, transferred_amount=0, amount=1, expiration=1, @@ -129,6 +132,7 @@ def make_mediated_transfer( identifier, nonce, token, + channel, transferred_amount, recipient, locksroot, @@ -143,6 +147,7 @@ def make_direct_transfer( identifier=0, nonce=1, token=ADDRESS, + channel=ADDRESS, transferred_amount=0, recipient=ADDRESS, locksroot=''): @@ -151,6 +156,7 @@ def make_direct_transfer( identifier, nonce, token, + channel, transferred_amount, recipient, locksroot, diff --git a/raiden/tests/utils/tester_client.py b/raiden/tests/utils/tester_client.py index 99e1281f30..aa92fc2042 100644 --- a/raiden/tests/utils/tester_client.py +++ b/raiden/tests/utils/tester_client.py @@ -17,6 +17,7 @@ isaddress, pex, privatekey_to_address, + sha3, ) from raiden.blockchain.abi import ( CONTRACT_MANAGER, @@ -144,8 +145,22 @@ def settled_block(self): def can_transfer(self): return self.netting_channel.can_transfer() - def update_transfer(self, first_transfer): - return self.netting_channel.update_transfer(first_transfer) + def update_transfer(self, partner_transfer): + nonce = partner_transfer.nonce + transferred_amount = partner_transfer.transferred_amount + locksroot = partner_transfer.locksroot + signature = partner_transfer.signature + + packed = partner_transfer.packed() + message_hash = sha3(packed.data[:-65]) + + return self.netting_channel.update_transfer( + nonce, + transferred_amount, + locksroot, + message_hash, + signature, + ) def withdraw(self, unlock_proofs): return self.netting_channel.withdraw(unlock_proofs) @@ -658,32 +673,59 @@ def detail(self, our_address): data[2], )) - def close(self, their_transfer): - their_encoded = '' - if their_transfer is not None: - their_encoded = their_transfer.encode() - + def close(self, nonce, transferred_amount, locksroot, extra_hash, signature): # this transaction may fail if there is a race to close the channel - self.proxy.close(their_encoded) - self.tester_state.mine(number_of_blocks=1) + log.info( - 'close called', - contract=pex(self.address), - their_transfer=their_transfer, + 'closing channel', + contract=pex(self.proxy.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, ) - def update_transfer(self, first_transfer): - if first_transfer is not None: - first_encoded = first_transfer.encode() - self.proxy.updateTransfer(first_encoded) - self.tester_state.mine(number_of_blocks=1) + self.proxy.close( + nonce, + transferred_amount, + locksroot, + extra_hash, + signature, + ) + self.tester_state.mine(number_of_blocks=1) log.info( - 'update_transfer called', - contract=pex(self.address), - first_transfer=first_transfer + 'close sucessfull', + contract=pex(self.proxy.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, ) + def update_transfer(self, nonce, transferred_amount, locksroot, extra_hash, signature): + if signature: + self.proxy.updateTransfer( + nonce, + transferred_amount, + locksroot, + extra_hash, + signature, + ) + self.tester_state.mine(number_of_blocks=1) + + log.info( + 'update_transfer called', + contract=pex(self.address), + nonce=nonce, + transferred_amount=transferred_amount, + locksroot=locksroot, + extra_hash=extra_hash, + signature=signature, + ) + def withdraw(self, unlock_proofs): # force a list to get the length (could be a generator) unlock_proofs = list(unlock_proofs)