class BalanceProof(Message): """ A Balance Proof If transferred_amount, locked_amount and locksroot are set, balance_proof hash is computed using these values. Otherwise a value stored in _balance_hash is returned. Serialization will also add these items only if each of transferred_amount, locked_amount and locksroot is set. """ def __init__( self, channel_identifier: ChannelIdentifier, token_network_address: Address, balance_hash: str = None, nonce: int = 0, additional_hash: str = '0x%064x' % 0, chain_id: int = 1, signature: str = None, transferred_amount: int = None, locked_amount: int = 0, locksroot: str = '0x%064x' % 0, ) -> None: super().__init__() assert isinstance(channel_identifier, T_ChannelIdentifier) assert is_address(token_network_address) self._type = 'BalanceProof' self.channel_identifier = channel_identifier self.token_network_address = token_network_address self._balance_hash = balance_hash self.additional_hash = additional_hash self.nonce = nonce self.chain_id = chain_id self.signature = signature if transferred_amount and locked_amount and locksroot and balance_hash: assert 0 <= transferred_amount <= UINT256_MAX assert 0 <= locked_amount <= UINT256_MAX assert self.hash_balance_data( transferred_amount, locked_amount, locksroot, ) == balance_hash self.transferred_amount = transferred_amount self.locked_amount = locked_amount self.locksroot = locksroot def serialize_data(self) -> dict: result = { 'channel_identifier': self.channel_identifier, 'token_network_address': self.token_network_address, 'balance_hash': self.balance_hash, 'additional_hash': self.additional_hash, 'nonce': self.nonce, 'chain_id': self.chain_id, 'signature': self.signature, } if None not in (self.transferred_amount, self.locked_amount, self.locksroot): result['transferred_amount'] = self.transferred_amount result['locked_amount'] = self.locked_amount result['locksroot'] = self.locksroot return result def serialize_bin(self): return pack_data([ 'bytes32', 'uint256', 'bytes32', 'bytes32', 'address', 'uint256', ], [ decode_hex(self.balance_hash), self.nonce, decode_hex(self.additional_hash), decode_hex(self.channel_identifier), self.token_network_address, self.chain_id, ]) @classmethod def deserialize(cls, data): jsonschema.validate(data, BALANCE_PROOF_SCHEMA) result = cls( data['channel_identifier'], data['token_network_address'], balance_hash=data['balance_hash'], nonce=data['nonce'], additional_hash=data['additional_hash'], chain_id=data['chain_id'], signature=data['signature'], transferred_amount=data.get('transferred_amount', None), locked_amount=data.get('locked_amount', None), locksroot=data.get('locksroot', None), ) return result token_network_address = address_property('_contract') # type: ignore json_schema = BALANCE_PROOF_SCHEMA @property def balance_hash(self) -> str: if self._balance_hash: return self._balance_hash if None not in (self.transferred_amount, self.locked_amount, self.locksroot): assert isinstance(self.transferred_amount, int) return encode_hex( self.hash_balance_data( self.transferred_amount, self.locked_amount, self.locksroot, ), ) raise ValueError("Can't compute balance hash") @balance_hash.setter def balance_hash(self, value) -> None: self._balance_hash = value @property def signer(self) -> str: signer = eth_verify( decode_hex(self.signature), self.serialize_bin(), ) return to_checksum_address(signer) @staticmethod def hash_balance_data( transferred_amount: int, locked_amount: int, locksroot: str, ) -> str: return Web3.soliditySha3( ['uint256', 'uint256', 'bytes32'], [transferred_amount, locked_amount, locksroot], )
class PathsRequest(Message): """ A message to request a path from PFS. It is sent from a raiden node to the PFS. """ def __init__( self, token_network_address: Address, source_address: Address, target_address: Address, value: int, chain_id: int, num_paths: int, nonce: int, signature: str = None, ) -> None: super().__init__() assert is_address(token_network_address) assert is_address(source_address) assert is_address(target_address) assert 0 <= value <= UINT256_MAX assert 0 <= num_paths <= UINT256_MAX assert 0 <= nonce <= UINT256_MAX self._type = 'PathsRequest' self.token_network_address = token_network_address self.source_address = source_address self.target_address = target_address self.value = value self.num_paths = num_paths self.chain_id = chain_id self.nonce = nonce self.signature = signature def serialize_data(self) -> Dict: return { 'token_network_address': self.token_network_address, 'source_address': self.source_address, 'target_address': self.target_address, 'value': self.value, 'num_paths': self.num_paths, 'chain_id': self.chain_id, 'nonce': self.nonce, 'signature': self.signature, } def serialize_bin(self): """Returns PathsRequest serialized to binary""" return pack_data([ 'address', 'address', 'address', 'uint256', 'uint256', 'uint256', 'uint256', ], [ self.token_network_address, self.source_address, self.target_address, self.value, self.num_paths, self.chain_id, self.nonce, ]) @classmethod def deserialize(cls, data): jsonschema.validate(data, PATHS_REQUEST_SCHEMA) ret = cls( token_network_address=data['token_network_address'], source_address=data['source_address'], target_address=data['target_address'], value=data['value'], num_paths=data['num_paths'], chain_id=data['chain_id'], nonce=data['nonce'], signature=data['signature'], ) return ret @property def signer(self) -> str: signer = eth_verify( decode_hex(self.signature), self.serialize_bin(), ) return to_checksum_address(signer) token_network_address = address_property('_contract') # type: ignore json_schema = PATHS_REQUEST_SCHEMA
class MonitorRequest(Message): """Message sent by a Raiden node to the MS. It cointains all data required to call MSC """ monitor_address = address_property('_monitor_address') # type: ignore _type = 'MonitorRequest' def __init__( self, balance_proof: BalanceProof, non_closing_signature: str = None, reward_proof_signature: bytes = None, # bytes reward_amount: int = None, # uint192 monitor_address: Address = None, ) -> None: assert non_closing_signature is None or len( decode_hex(non_closing_signature)) == 65 assert reward_amount is None or (reward_amount >= 0) and (reward_amount <= UINT192_MAX) # todo: validate reward proof signature assert is_address(monitor_address) assert isinstance(balance_proof, BalanceProof) self._balance_proof = balance_proof self.non_closing_signature = non_closing_signature self.reward_proof_signature = reward_proof_signature self.reward_amount = reward_amount self.monitor_address = monitor_address def serialize_data(self): msg = self.__dict__.copy() msg.pop('_balance_proof') msg['monitor_address'] = msg.pop('_monitor_address') msg['balance_proof'] = self.balance_proof.serialize_data() msg['reward_proof_signature'] = self.reward_proof_signature return msg def serialize_reward_proof(self): """Return reward proof data serialized to binary""" return pack_data([ 'uint256', 'uint256', 'address', 'uint256', 'uint256', ], [ self.balance_proof.channel_identifier, self.reward_amount, self.balance_proof.token_network_address, self.balance_proof.chain_id, self.balance_proof.nonce, ]) @classmethod def deserialize(cls, data): jsonschema.validate(data, MONITOR_REQUEST_SCHEMA) balance_proof = BalanceProof.deserialize(data['balance_proof']) result = cls( balance_proof, data['non_closing_signature'], data['reward_proof_signature'], data['reward_amount'], data['monitor_address'], ) return result @property def balance_proof(self): return self._balance_proof @property def reward_proof_signer(self) -> str: signer = eth_recover( data=self.serialize_reward_proof(), signature=decode_hex(self.reward_proof_signature), ) return to_checksum_address(signer) @property def non_closing_signer(self) -> str: serialized = self.balance_proof.serialize_bin( msg_type=MessageTypeId.BALANCE_PROOF_UPDATE) signer = eth_recover( data=serialized + decode_hex(self.balance_proof.signature), signature=decode_hex(self.non_closing_signature), ) return to_checksum_address(signer)
class FeeInfo(Message): """ A message to update the fee. It is sent from a raiden node to the PFS. """ def __init__( self, token_network_address: Address, channel_identifier: ChannelIdentifier, chain_id: int = 1, nonce: int = 0, relative_fee: int = 0, # in parts per million signature: str = None, ) -> None: """ Creates a new FeeInfo message Args: relative_fee: The fee defined in parts per million, e.g. a value of 10000 corresponds to a relative fee of one percent. """ super().__init__() assert isinstance(channel_identifier, T_ChannelIdentifier) assert is_address(token_network_address) self._type = 'FeeInfo' self.token_network_address = token_network_address self.channel_identifier = channel_identifier self.chain_id = chain_id self.nonce = nonce self.relative_fee = relative_fee self.signature = signature def serialize_data(self) -> Dict: return { 'token_network_address': self.token_network_address, 'channel_identifier': self.channel_identifier, 'chain_id': self.chain_id, 'nonce': self.nonce, 'relative_fee': self.relative_fee, 'signature': self.signature, } def serialize_bin(self) -> bytes: """Return FeeInfo serialized to binary""" return pack_data([ 'address', 'bytes32', 'uint256', 'uint256', 'uint256', ], [ self.token_network_address, decode_hex(self.channel_identifier), self.chain_id, self.nonce, self.relative_fee, ]) @classmethod def deserialize(cls, data): jsonschema.validate(data, FEE_INFO_SCHEMA) ret = cls( token_network_address=data['token_network_address'], channel_identifier=data['channel_identifier'], chain_id=data['chain_id'], nonce=data['nonce'], relative_fee=data['relative_fee'], signature=data['signature'], ) return ret @property def signer(self) -> str: signer = eth_verify( decode_hex(self.signature), self.serialize_bin(), ) return to_checksum_address(signer) token_network_address = address_property('_contract') # type: ignore json_schema = FEE_INFO_SCHEMA
class PathsReply(Message): """ A reply message from PFS to a client. It is sent from PFS to a raiden node . """ def __init__( self, token_network_address: Address, target_address: Address, value: int, chain_id: int, nonce: int, paths_and_fees: list = None, signature: str = None, ) -> None: super().__init__() assert is_address(token_network_address) assert is_address(target_address) assert 0 <= value <= UINT256_MAX assert 0 <= nonce <= UINT256_MAX self._type = 'PathsReply' self.token_network_address = token_network_address self.target_address = target_address self.value = value self.chain_id = chain_id self.nonce = nonce self.paths_and_fees = paths_and_fees self.signature = signature def serialize_data(self) -> Dict: return { 'token_network_address': self.token_network_address, 'target_address': self.target_address, 'value': self.value, 'chain_id': self.chain_id, 'nonce': self.nonce, 'paths_and_fees': self.paths_and_fees, 'signature': self.signature, } def serialize_bin(self): """Returns PathsReply serialized to binary""" return pack_data([ 'address', 'address', 'uint256', 'uint256', 'uint256', 'array', 'signature', ], [ self.token_network_address, self.target_address, self.value, self.chain_id, self.nonce, self.paths_and_fees, self.signature, ]) @classmethod def deserialize(cls, data): jsonschema.validate(data, PATHS_REPLY_SCHEMA) ret = cls( token_network_address=data['token_network_address'], target_address=data['target_address'], value=data['value'], chain_id=data['chain_id'], nonce=data['nonce'], paths_and_fees=data['paths_and_fees'], signature=data['signature'], ) return ret @property def signer(self) -> str: signer = eth_recover( data=self.serialize_bin(), signature=decode_hex(self.signature), ) return to_checksum_address(signer) token_network_address = address_property('_contract') # type: ignore json_schema = PATHS_REPLY_SCHEMA
class BalanceProof(Message): """ A Balance Proof This optionally includse the data for calculating the balance_hash. """ def __init__( self, channel_identifier: ChannelIdentifier, token_network_address: Address, balance_hash: str = None, nonce: int = 0, additional_hash: str = None, chain_id: int = 1, signature: str = None, transferred_amount: int = None, locked_amount: int = None, locksroot: str = None, ) -> None: super().__init__() assert channel_identifier > 0 assert is_address(token_network_address) self._type = 'BalanceProof' self.channel_identifier = channel_identifier self.token_network_address = token_network_address self._balance_hash = balance_hash self.additional_hash = additional_hash self.nonce = nonce self.chain_id = chain_id self.signature = signature if transferred_amount and locked_amount and locksroot and balance_hash: assert 0 <= transferred_amount <= UINT256_MAX assert 0 <= locked_amount <= UINT256_MAX assert self.hash_balance_data(transferred_amount, locked_amount, locksroot) == balance_hash self.transferred_amount = transferred_amount self.locked_amount = locked_amount self.locksroot = locksroot def serialize_data(self) -> dict: result = { 'channel_identifier': self.channel_identifier, 'token_network_address': self.token_network_address, 'balance_hash': self.balance_hash, 'additional_hash': self.additional_hash, 'nonce': self.nonce, 'chain_id': self.chain_id, 'signature': self.signature, } if None not in (self.transferred_amount, self.locked_amount, self.locksroot): result['transferred_amount'] = self.transferred_amount result['locked_amount'] = self.locked_amount result['locksroot'] = self.locksroot return result def serialize_bin(self): return pack_data( ['bytes32', 'uint256', 'bytes32', 'uint256', 'address', 'uint256'], [ decode_hex(self.balance_hash), self.nonce, decode_hex(self.additional_hash), self.channel_identifier, self.token_network_address, self.chain_id ]) @classmethod def deserialize(cls, data): jsonschema.validate(data, BALANCE_PROOF_SCHEMA) result = cls( data['channel_identifier'], data['token_network_address'], balance_hash=data['balance_hash'], nonce=data['nonce'], additional_hash=data['additional_hash'], chain_id=data['chain_id'], signature=data['signature'], transferred_amount=data.get('transferred_amount', None), locked_amount=data.get('locked_amount', None), locksroot=data.get('locksroot', None), ) return result token_network_address = address_property('_contract') # type: ignore json_schema = BALANCE_PROOF_SCHEMA @property def balance_hash(self) -> str: if self._balance_hash: return self._balance_hash if None not in (self.transferred_amount, self.locked_amount, self.locksroot): return encode_hex( self.hash_balance_data(self.transferred_amount, self.locked_amount, self.locksroot)) raise ValueError("Can't compute balance hash") @property def signer(self) -> str: return eth_verify(decode_hex(self.signature), self.serialize_bin()) @staticmethod def hash_balance_data(transferred_amount: int, locked_amount: int, locksroot: str) -> str: return Web3.soliditySha3( ['uint256', 'uint256', 'bytes32'], [transferred_amount, locked_amount, locksroot])