def make_from_route_from_counter(counter): from_channel = factories.create( factories.NettingChannelStateProperties( canonical_identifier=factories.make_canonical_identifier(), token_address=factories.make_token_address(), partner_state=factories.NettingChannelEndStateProperties( balance=next(counter), address=factories.HOP1), )) from_hop = factories.make_hop_from_channel(from_channel) expiration = BlockExpiration(factories.UNIT_REVEAL_TIMEOUT + 1) from_transfer = factories.make_signed_transfer_for( from_channel, factories.LockedTransferSignedStateProperties( transferred_amount=TokenAmount(0), canonical_identifier=factories.make_canonical_identifier( token_network_address=from_channel.token_network_address), amount=TokenAmount(1), expiration=expiration, secret=sha3(factories.make_secret(next(counter))), initiator=factories.make_initiator_address(), target=factories.make_target_address(), payment_identifier=next(counter), sender=factories.HOP1, pkey=factories.HOP1_KEY, ), ) return from_hop, from_transfer
def test_restore_queueids_to_queues(chain_state, netting_channel_state): """Test that withdraw messages are restorable if they exist in chain_state.queueids_to_queues. """ recipient = netting_channel_state.partner_state.address queue_identifier = QueueIdentifier( recipient=recipient, canonical_identifier=netting_channel_state.canonical_identifier) msg_args = dict( recipient=recipient, canonical_identifier=netting_channel_state.canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=WithdrawAmount(1), participant=recipient, expiration=BlockExpiration(10), nonce=Nonce(15), ) messages = [ SendWithdrawRequest(**msg_args), SendWithdrawConfirmation(**msg_args), SendWithdrawExpired(**msg_args), ] chain_state.queueids_to_queues[queue_identifier] = messages serialized_chain_state = JSONSerializer.serialize(chain_state) deserialized_chain_state = JSONSerializer.deserialize( serialized_chain_state) assert chain_state == deserialized_chain_state
def test_events_loaded_from_storage_should_deserialize(tmp_path): filename = Path(f"{tmp_path}/v{RAIDEN_DB_VERSION}_log.db") storage = SerializedSQLiteStorage(filename, serializer=JSONSerializer()) # Satisfy the foreign-key constraint for state change ID ids = storage.write_state_changes([ Block( block_number=BlockNumber(1), gas_limit=BlockGasLimit(1), block_hash=factories.make_block_hash(), ) ]) canonical_identifier = factories.make_canonical_identifier() recipient = factories.make_address() participant = factories.make_address() event = SendWithdrawRequest( recipient=recipient, canonical_identifier=canonical_identifier, message_identifier=factories.make_message_identifier(), total_withdraw=WithdrawAmount(1), participant=participant, expiration=BlockExpiration(10), nonce=Nonce(15), ) storage.write_events([(ids[0], event)]) stored_events = storage.get_events() assert stored_events[0] == event
def from_dict(cls, data: Dict[str, Any]) -> 'ContractSendSecretReveal': restored = cls( expiration=BlockExpiration(int(data['expiration'])), secret=Secret(serialization.deserialize_bytes(data['secret'])), ) return restored
def from_dict(cls, data: Dict[str, Any]) -> 'ContractSendChannelUpdateTransfer': restored = cls( expiration=BlockExpiration(int(data['expiration'])), balance_proof=data['balance_proof'], triggered_by_block_hash=BlockHash(deserialize_bytes(data['triggered_by_block_hash'])), ) return restored
def from_bytes(cls, serialized: bytes) -> "Lock": return cls( expiration=BlockExpiration( int.from_bytes(serialized[:32], byteorder="big")), amount=PaymentWithFeeAmount( int.from_bytes(serialized[32:64], byteorder="big")), secrethash=SecretHash(serialized[64:]), )
def test_locked_transfer_secret_registered_onchain( raiden_network, token_addresses, secret_registry_address, retry_timeout ): app0 = raiden_network[0] token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address ) amount = TokenAmount(1) target = factories.UNIT_TRANSFER_INITIATOR identifier = PaymentID(1) transfer_secret = make_secret() secret_registry_proxy = app0.raiden.proxy_manager.secret_registry( secret_registry_address, block_identifier=chain_state.block_hash ) secret_registry_proxy.register_secret(secret=transfer_secret) # Wait until our node has processed the block that the secret registration was mined at block_number = app0.raiden.get_block_number() wait_for_block( raiden=app0.raiden, block_number=block_number + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS, retry_timeout=retry_timeout, ) # Test that sending a transfer with a secret already registered on-chain fails with pytest.raises(RaidenUnrecoverableError): app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=transfer_secret, ) # Test that receiving a transfer with a secret already registered on chain fails expiration = BlockExpiration(9999) locked_transfer = factories.create( factories.LockedTransferProperties( amount=amount, target=app0.raiden.address, expiration=expiration, secret=transfer_secret, ) ) message_handler = MessageHandler() message_handler.handle_message_lockedtransfer(app0.raiden, locked_transfer) state_changes = app0.raiden.wal.storage.get_statechanges_by_range(RANGE_ALL_STATE_CHANGES) transfer_statechange_dispatched = search_for_item( state_changes, ActionInitMediator, {} ) or search_for_item(state_changes, ActionInitTarget, {}) assert not transfer_statechange_dispatched
def from_dict(cls, data: Dict[str, Any]) -> "ContractSendSecretReveal": restored = cls( expiration=BlockExpiration(int(data["expiration"])), secret=Secret(serialization.deserialize_bytes(data["secret"])), triggered_by_block_hash=BlockHash( deserialize_bytes(data["triggered_by_block_hash"])), ) return restored
def from_dict(cls, data: Dict[str, Any]) -> 'ReceiveSecretRequest': instance = cls( payment_identifier=PaymentID(int(data['payment_identifier'])), amount=PaymentAmount(int(data['amount'])), expiration=BlockExpiration(int(data['expiration'])), secrethash=SecretHash(deserialize_bytes(data['secrethash'])), sender=to_canonical_address(data['sender']), ) instance.revealsecret = data['revealsecret'] return instance
def from_dict(cls, data: Dict[str, Any]) -> "ReceiveSecretRequest": instance = cls( payment_identifier=PaymentID(int(data["payment_identifier"])), amount=PaymentAmount(int(data["amount"])), expiration=BlockExpiration(int(data["expiration"])), secrethash=SecretHash(deserialize_bytes(data["secrethash"])), sender=to_canonical_address(data["sender"]), ) instance.revealsecret = data["revealsecret"] return instance
def from_dict( cls, data: Dict[str, Any]) -> "ContractSendChannelUpdateTransferLight": restored = cls(expiration=BlockExpiration(int(data["expiration"])), balance_proof=data["balance_proof"], triggered_by_block_hash=BlockHash( deserialize_bytes(data["triggered_by_block_hash"])), lc_bp_signature=data["lc_bp_signature"], lc_address=to_canonical_address(data["lc_address"])) return restored
def make_message(sign: bool = True) -> Message: message = SecretRequest( message_identifier=make_message_identifier(), payment_identifier=PaymentID(1), secrethash=factories.UNIT_SECRETHASH, amount=PaymentAmount(1), expiration=BlockExpiration(10), signature=EMPTY_SIGNATURE, ) if sign: message.sign(LocalSigner(factories.HOP1_KEY)) return message
def from_dict(cls, data: Dict[str, Any]) -> 'SendSecretRequest': restored = cls( recipient=to_canonical_address(data['recipient']), channel_identifier=ChannelID(int(data['channel_identifier'])), message_identifier=MessageID(int(data['message_identifier'])), payment_identifier=PaymentID(int(data['payment_identifier'])), amount=TokenAmount(int(data['amount'])), expiration=BlockExpiration(int(data['expiration'])), secrethash=SecretHash(serialization.deserialize_bytes(data['secrethash'])), ) return restored
def from_dict(cls, data: Dict[str, Any]) -> "SendSecretRequest": restored = cls( recipient=to_canonical_address(data["recipient"]), channel_identifier=ChannelID(int(data["channel_identifier"])), message_identifier=MessageID(int(data["message_identifier"])), payment_identifier=PaymentID(int(data["payment_identifier"])), amount=PaymentWithFeeAmount(int(data["amount"])), expiration=BlockExpiration(int(data["expiration"])), secrethash=deserialize_secret_hash(data["secrethash"]), ) return restored
class UnlockPartialProofState(State): """ Stores the lock along with its unlocking secret. """ lock: HashTimeLockState secret: Secret = field(repr=False) amount: PaymentWithFeeAmount = field(repr=False, default=PaymentWithFeeAmount(0)) expiration: BlockExpiration = field(repr=False, default=BlockExpiration(0)) secrethash: SecretHash = field(repr=False, default=EMPTY_SECRETHASH) encoded: EncodedData = field(init=False, repr=False) def __post_init__(self) -> None: typecheck(self.lock, HashTimeLockState) typecheck(self.secret, T_Secret) self.amount = self.lock.amount self.expiration = self.lock.expiration self.secrethash = self.lock.secrethash self.encoded = self.lock.encoded
def make_iou( pfs_config: PFSConfig, our_address: Address, one_to_n_address: OneToNAddress, privkey: PrivateKey, block_number: BlockNumber, chain_id: ChainID, offered_fee: TokenAmount, ) -> IOU: expiration = BlockExpiration(block_number + pfs_config.iou_timeout) iou = IOU( sender=our_address, receiver=pfs_config.info.payment_address, one_to_n_address=one_to_n_address, amount=offered_fee, expiration_block=expiration, chain_id=chain_id, ) iou.sign(privkey) return iou
def _new_mediator_transfer(self, initiator_address, target_address, payment_id, amount, secret, our_address) -> LockedTransferSignedState: initiator_pkey = self.address_to_privkey[initiator_address] balance_proof_data = self._update_balance_proof_data( initiator_address, amount, self.block_number + 10, secret, our_address) self.secrethash_to_secret[sha256_secrethash(secret)] = secret return factories.create( factories.LockedTransferSignedStateProperties( # type: ignore **balance_proof_data.properties.__dict__, amount=amount, expiration=BlockExpiration(self.block_number + 10), payment_identifier=payment_id, secret=secret, initiator=initiator_address, target=target_address, token=self.token_id, sender=initiator_address, recipient=our_address, pkey=initiator_pkey, message_identifier=MessageID(1), ))
def test_regression_multiple_revealsecret( raiden_network: List[App], token_addresses: List[TokenAddress]) -> None: """ Multiple RevealSecret messages arriving at the same time must be handled properly. Unlock handling followed these steps: The Unlock message arrives The secret is registered The channel is updated and the correspoding lock is removed * A balance proof for the new channel state is created and sent to the payer The channel is unregistered for the given secrethash The step marked with an asterisk above introduced a context-switch. This allowed a second Reveal Unlock message to be handled before the channel was unregistered. And because the channel was already updated an exception was raised for an unknown secret. """ app0, app1 = raiden_network token = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token) assert token_network_address channelstate_0_1 = get_channelstate(app0, app1, token_network_address) payment_identifier = PaymentID(1) secret, secrethash = make_secret_with_hash() expiration = BlockExpiration(app0.raiden.get_block_number() + 100) lock_amount = PaymentWithFeeAmount(10) lock = Lock(amount=lock_amount, expiration=expiration, secrethash=secrethash) nonce = Nonce(1) transferred_amount = TokenAmount(0) mediated_transfer = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=nonce, token_network_address=token_network_address, token=token, channel_identifier=channelstate_0_1.identifier, transferred_amount=transferred_amount, locked_amount=LockedAmount(lock_amount), recipient=app1.raiden.address, locksroot=Locksroot(lock.lockhash), lock=lock, target=TargetAddress(app1.raiden.address), initiator=InitiatorAddress(app0.raiden.address), signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[ RouteMetadata(route=[app0.raiden.address, app1.raiden.address]) ]), ) app0.raiden.sign(mediated_transfer) app1.raiden.on_messages([mediated_transfer]) reveal_secret = RevealSecret(message_identifier=make_message_identifier(), secret=secret, signature=EMPTY_SIGNATURE) app0.raiden.sign(reveal_secret) token_network_address = channelstate_0_1.token_network_address unlock = Unlock( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=Nonce(mediated_transfer.nonce + 1), token_network_address=token_network_address, channel_identifier=channelstate_0_1.identifier, transferred_amount=TokenAmount(lock_amount), locked_amount=LockedAmount(0), locksroot=LOCKSROOT_OF_NO_LOCKS, secret=secret, signature=EMPTY_SIGNATURE, ) app0.raiden.sign(unlock) messages = [unlock, reveal_secret] receive_method = app1.raiden.on_messages wait = set( gevent.spawn_later(0.1, receive_method, [data]) for data in messages) gevent.joinall(wait, raise_error=True)
) from raiden.transfer.mediated_transfer.state import MediatorTransferState from raiden.transfer.mediated_transfer.state_change import ( ActionInitMediator, ReceiveLockExpired, ReceiveSecretReveal, ReceiveTransferRefund, ) from raiden.transfer.state import NetworkState, message_identifier_from_prng from raiden.transfer.state_change import Block, ContractReceiveSecretReveal from raiden.utils.signer import LocalSigner from raiden.utils.typing import BlockExpiration LONG_EXPIRATION = factories.create_properties( factories.LockedTransferSignedStateProperties( expiration=BlockExpiration(30))) def test_payer_enter_danger_zone_with_transfer_payed(): """ A mediator may have paid the next hop (payee), and didn't get paid by the previous hop (payer). When this happens, an assertion must not be hit, because it means the transfer must be unlocked on-chain. Issue: https://github.com/raiden-network/raiden/issues/1013 """ block_number = 5 pseudo_random_generator = random.Random() channels = factories.mediator_make_channel_pair()
def make_hash_time_lock_state(amount) -> HashTimeLockState: return HashTimeLockState(amount=amount, expiration=BlockExpiration(5), secrethash=factories.UNIT_SECRETHASH)
def get_initial_lock_expiration( block_number: BlockNumber, reveal_timeout: BlockTimeout, ) -> BlockExpiration: """ Returns the expiration used for all hash-time-locks in transfer. """ return BlockExpiration(block_number + reveal_timeout * 2)
def _withdraw_participant_left_capacity_from_channel( address_to_candidate: Dict[Address, ReclamationCandidate], channel: dict, token_network: TokenNetwork, current_confirmed_head: BlockIdentifier, ) -> None: """ Withdraw all tokens in channel to channel["participant1"] """ assert token_network.client.address == channel["participant1"] # Check if channel still has deposits details = token_network.detail_participants( participant1=to_canonical_address(channel["participant1"]), participant2=to_canonical_address(channel["participant2"]), block_identifier=current_confirmed_head, channel_identifier=channel["channel_identifier"], ) new_withdraw = WithdrawAmount(details.our_details.deposit - details.our_details.withdrawn + details.partner_details.deposit - details.partner_details.withdrawn) assert new_withdraw >= 0, "negative withdrawn should never happen." if new_withdraw == 0: log.info( "Participant has no left over capacity in the channel. Skipping channel.", channel=channel, ) return partner_candidate = address_to_candidate.get( details.partner_details.address) if partner_candidate is None: log.error( "Both participants must be in list of reclamation_candidates. Skipping channel.", channel=channel, ) return expiration_block = BlockExpiration(100000000000000) total_withdraw = WithdrawAmount(details.our_details.withdrawn + new_withdraw) packed_withdraw = pack_withdraw( canonical_identifier=CanonicalIdentifier( chain_identifier=token_network.chain_id(), token_network_address=token_network.address, channel_identifier=channel["channel_identifier"], ), participant=to_canonical_address(channel["participant1"]), total_withdraw=total_withdraw, expiration_block=expiration_block, ) privkey = token_network.client.privkey try: token_network.set_total_withdraw( given_block_identifier=current_confirmed_head, channel_identifier=channel["channel_identifier"], total_withdraw=total_withdraw, expiration_block=expiration_block, participant=to_canonical_address(channel["participant1"]), partner=to_canonical_address(channel["participant2"]), participant_signature=LocalSigner(privkey).sign(packed_withdraw), partner_signature=LocalSigner( partner_candidate.privkey).sign(packed_withdraw), ) except InsufficientEth: log.warning("Not enough ETH to withdraw", channel=channel) else: log.info("Withdraw successful", channel=channel, amount=new_withdraw)
def handle_block( initiator_state: InitiatorTransferState, state_change: Block, channel_state: NettingChannelState, pseudo_random_generator: random.Random, ) -> TransitionResult[Optional[InitiatorTransferState]]: """ Checks if the lock has expired, and if it has sends a remove expired lock and emits the failing events. """ secrethash = initiator_state.transfer.lock.secrethash locked_lock = channel_state.our_state.secrethashes_to_lockedlocks.get( secrethash) if not locked_lock: if channel_state.partner_state.secrethashes_to_lockedlocks.get( secrethash): return TransitionResult(initiator_state, list()) else: # if lock is not in our or our partner's locked locks then the # task can go return TransitionResult(None, list()) lock_expiration_threshold = BlockExpiration( locked_lock.expiration + DEFAULT_WAIT_BEFORE_LOCK_REMOVAL) lock_has_expired = channel.is_lock_expired( end_state=channel_state.our_state, lock=locked_lock, block_number=state_change.block_number, lock_expiration_threshold=lock_expiration_threshold, ) events: List[Event] = list() if lock_has_expired and initiator_state.transfer_state != "transfer_expired": is_channel_open = channel.get_status( channel_state) == ChannelState.STATE_OPENED if is_channel_open: expired_lock_events = channel.send_lock_expired( channel_state=channel_state, locked_lock=locked_lock, pseudo_random_generator=pseudo_random_generator, ) events.extend(expired_lock_events) if initiator_state.received_secret_request: reason = "bad secret request message from target" else: reason = "lock expired" transfer_description = initiator_state.transfer_description payment_identifier = transfer_description.payment_identifier # TODO: When we introduce multiple transfers per payment this needs to be # reconsidered. As we would want to try other routes once a route # has failed, and a transfer failing does not mean the entire payment # would have to fail. # Related issue: https://github.com/raiden-network/raiden/issues/2329 payment_failed = EventPaymentSentFailed( token_network_registry_address=transfer_description. token_network_registry_address, token_network_address=transfer_description.token_network_address, identifier=payment_identifier, target=transfer_description.target, reason=reason, ) route_failed = EventRouteFailed( secrethash=secrethash, route=initiator_state.route.route, token_network_address=transfer_description.token_network_address, ) unlock_failed = EventUnlockFailed( identifier=payment_identifier, secrethash=initiator_state.transfer_description.secrethash, reason=reason, ) lock_exists = channel.lock_exists_in_either_channel_side( channel_state=channel_state, secrethash=secrethash) initiator_state.transfer_state = "transfer_expired" return TransitionResult( # If the lock is either in our state or partner state we keep the # task around to wait for the LockExpired messages to sync. # Check https://github.com/raiden-network/raiden/issues/3183 initiator_state if lock_exists else None, events + [payment_failed, route_failed, unlock_failed], ) else: return TransitionResult(initiator_state, events)