def handle_contract_send_channelunlock( raiden: "RaidenService", chain_state: ChainState, channel_unlock_event: ContractSendChannelBatchUnlock, ): assert raiden.wal, "The Raiden Service must be initialize to handle events" canonical_identifier = channel_unlock_event.canonical_identifier token_network_identifier = canonical_identifier.token_network_address channel_identifier = canonical_identifier.channel_identifier participant = channel_unlock_event.participant payment_channel: PaymentChannel = raiden.chain.payment_channel( canonical_identifier=canonical_identifier ) channel_state = get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_id=TokenNetworkID(token_network_identifier), partner_address=participant, ) if not channel_state: # channel was cleaned up already due to an unlock raise RaidenUnrecoverableError( f"Failed to find channel state with partner:" f"{to_checksum_address(participant)}, token_network:pex(token_network_identifier)" ) our_address = channel_state.our_state.address our_locksroot = channel_state.our_state.onchain_locksroot partner_address = channel_state.partner_state.address partner_locksroot = channel_state.partner_state.onchain_locksroot # we want to unlock because there are on-chain unlocked locks search_events = our_locksroot != EMPTY_HASH # we want to unlock, because there are unlocked/unclaimed locks search_state_changes = partner_locksroot != EMPTY_HASH if not search_events and not search_state_changes: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( "Onchain unlock already mined", canonical_identifier=canonical_identifier, channel_identifier=canonical_identifier.channel_identifier, participant=to_checksum_address(participant), ) return if search_state_changes: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=partner_locksroot, sender=partner_address, ) state_change_identifier = state_change_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f"Failed to find state that matches the current channel locksroots. " f"chain_id:{raiden.chain.network_id} " f"token_network:{to_checksum_address(token_network_identifier)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} " ) restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) assert restored_channel_state is not None gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = ( restored_channel_state.partner_state.address == participant and gain.from_partner_locks == 0 ) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.partner_state, participant=our_address, partner=partner_address, ) if search_events: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=our_locksroot, recipient=partner_address, ) state_change_identifier = event_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f"Failed to find event that match current channel locksroots. " f"chain_id:{raiden.chain.network_id} " f"token_network:{to_checksum_address(token_network_identifier)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} " ) restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) assert restored_channel_state is not None gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = ( restored_channel_state.our_state.address == participant and gain.from_our_locks == 0 ) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.our_state, participant=partner_address, partner=our_address, )
def handle_contract_send_channelunlock( self, raiden: RaidenService, channel_unlock_event: ContractSendChannelBatchUnlock, ): token_network_identifier = channel_unlock_event.token_network_identifier channel_identifier = channel_unlock_event.channel_identifier participant = channel_unlock_event.participant token_address = channel_unlock_event.token_address payment_channel: PaymentChannel = raiden.chain.payment_channel( token_network_address=token_network_identifier, channel_id=channel_identifier, ) token_network: TokenNetwork = payment_channel.token_network participants_details = token_network.detail_participants( participant1=raiden.address, participant2=participant, block_identifier='latest', channel_identifier=channel_identifier, ) our_details = participants_details.our_details our_locksroot = our_details.locksroot partner_details = participants_details.partner_details partner_locksroot = partner_details.locksroot is_partner_unlock = (partner_details.address == participant and partner_locksroot != EMPTY_HASH) is_our_unlock = (our_details.address == participant and our_locksroot != EMPTY_HASH) if is_partner_unlock: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, locksroot=partner_locksroot, sender=participants_details.partner_details.address, ) state_change_identifier = state_change_record.state_change_identifier elif is_our_unlock: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, locksroot=our_locksroot.balance_hash, ) state_change_identifier = event_record.state_change_identifier else: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( 'Onchain unlock already mined', token_address=token_address, channel_identifier=channel_identifier, participant=participant, ) return if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find state/event that match current channel locksroots. ' f'chain_id:{raiden.chain.network_id} ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'our_balance_hash:{to_hex(our_details.balance_hash)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ' f'partner_balancehash:{to_hex(partner_details.balance_hash)} ', ) # Replay state changes until a channel state is reached where # this channel state has the participants balance hash. restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=channel_identifier, state_change_identifier=state_change_identifier, ) our_state = restored_channel_state.our_state partner_state = restored_channel_state.partner_state if partner_state.address == participant: merkle_tree_leaves = get_batch_unlock(partner_state) elif our_state.address == participant: merkle_tree_leaves = get_batch_unlock(our_state) try: # LEFTODO: Supply a proper block id payment_channel.unlock( merkle_tree_leaves=merkle_tree_leaves, block_identifier='latest', ) except ChannelOutdatedError as e: log.error( str(e), node=pex(raiden.address), )
def handle_channel_batch_unlock(raiden: "RaidenService", event: Event): assert raiden.wal, "The Raiden Service must be initialize to handle events" token_network_identifier = event.originating_contract data = event.event_data args = data["args"] block_number = data["block_number"] block_hash = data["block_hash"] transaction_hash = data["transaction_hash"] participant1 = args["participant"] participant2 = args["partner"] locksroot = args["locksroot"] chain_state = views.state_from_raiden(raiden) token_network_state = views.get_token_network_by_identifier( chain_state, token_network_identifier) assert token_network_state is not None if participant1 == raiden.address: partner = participant2 elif participant2 == raiden.address: partner = participant1 else: log.debug( "Discarding unlock event, we're not part of it", participant1=pex(participant1), participant2=pex(participant2), ) return channel_identifiers = token_network_state.partneraddresses_to_channelidentifiers[ partner] canonical_identifier = None for channel_identifier in channel_identifiers: if partner == args["partner"]: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=CanonicalIdentifier( chain_identifier=raiden.chain.network_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), locksroot=locksroot, sender=partner, ) if state_change_record.state_change_identifier: canonical_identifier = state_change_record.data.balance_proof.canonical_identifier break elif partner == args["participant"]: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=CanonicalIdentifier( chain_identifier=raiden.chain.network_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), locksroot=locksroot, recipient=partner, ) if event_record.event_identifier: canonical_identifier = event_record.data.balance_proof.canonical_identifier break msg = ( f"Can not resolve channel_id for unlock with locksroot {pex(locksroot)} and " f"partner {pex(partner)}.") assert canonical_identifier is not None, msg unlock_state_change = ContractReceiveChannelBatchUnlock( transaction_hash=transaction_hash, canonical_identifier=canonical_identifier, participant=args["participant"], partner=args["partner"], locksroot=args["locksroot"], unlocked_amount=args["unlocked_amount"], returned_tokens=args["returned_tokens"], block_number=block_number, block_hash=block_hash, ) raiden.handle_and_track_state_change(unlock_state_change)
def handle_contract_send_channelunlock( self, raiden: RaidenService, channel_unlock_event: ContractSendChannelBatchUnlock, ): token_network_identifier = channel_unlock_event.token_network_identifier channel_identifier = channel_unlock_event.channel_identifier participant = channel_unlock_event.participant token_address = channel_unlock_event.token_address payment_channel: PaymentChannel = raiden.chain.payment_channel( token_network_address=token_network_identifier, channel_id=channel_identifier, ) token_network: TokenNetwork = payment_channel.token_network participants_details = token_network.detail_participants( participant1=raiden.address, participant2=participant, channel_identifier=channel_identifier, ) our_details = participants_details.our_details our_locksroot = our_details.locksroot partner_details = participants_details.partner_details partner_locksroot = partner_details.locksroot is_partner_unlock = ( partner_details.address == participant and partner_locksroot != EMPTY_HASH ) is_our_unlock = ( our_details.address == participant and our_locksroot != EMPTY_HASH ) if is_partner_unlock: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, locksroot=partner_locksroot, sender=participants_details.partner_details.address, ) state_change_identifier = state_change_record.state_change_identifier elif is_our_unlock: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, locksroot=our_locksroot.balance_hash, ) state_change_identifier = event_record.state_change_identifier else: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( 'Onchain unlock already mined', token_address=token_address, channel_identifier=channel_identifier, participant=participant, ) return if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find state/event that match current channel locksroots. ' f'chain_id:{raiden.chain.network_id} ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'our_balance_hash:{to_hex(our_details.balance_hash)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ' f'partner_balancehash:{to_hex(partner_details.balance_hash)} ', ) # Replay state changes until a channel state is reached where # this channel state has the participants balance hash. restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=channel_identifier, state_change_identifier=state_change_identifier, ) our_state = restored_channel_state.our_state partner_state = restored_channel_state.partner_state if partner_state.address == participant: merkle_tree_leaves = get_batch_unlock(partner_state) elif our_state.address == participant: merkle_tree_leaves = get_batch_unlock(our_state) try: payment_channel.unlock(merkle_tree_leaves) except ChannelOutdatedError as e: log.error( str(e), node=pex(raiden.address), )
def handle_contract_send_channelunlock( raiden: 'RaidenService', channel_unlock_event: ContractSendChannelBatchUnlock, ): token_network_identifier = channel_unlock_event.token_network_identifier channel_identifier = channel_unlock_event.channel_identifier canonical_identifier = CanonicalIdentifier( chain_identifier=raiden.chain.network_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ) participant = channel_unlock_event.participant token_address = channel_unlock_event.token_address payment_channel: PaymentChannel = raiden.chain.payment_channel( canonical_identifier=canonical_identifier, ) channel_state = get_channelstate_by_token_network_and_partner( chain_state=state_from_raiden(raiden), token_network_id=token_network_identifier, partner_address=participant, ) if not channel_state: # channel was cleaned up already due to an unlock raise RaidenUnrecoverableError( f'Failed to find channel state with partner ' f'{participant}, token_network:pex(token_network_identifier)', ) our_address = channel_state.our_state.address our_locksroot = channel_state.our_state.onchain_locksroot partner_address = channel_state.partner_state.address partner_locksroot = channel_state.partner_state.onchain_locksroot # we want to unlock because there are on-chain unlocked locks search_events = our_locksroot != EMPTY_HASH # we want to unlock, because there are unlocked/unclaimed locks search_state_changes = partner_locksroot != EMPTY_HASH if not search_events and not search_state_changes: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( 'Onchain unlock already mined', token_address=token_address, channel_identifier=canonical_identifier.channel_identifier, participant=participant, ) return if search_state_changes: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=partner_locksroot, sender=partner_address, ) state_change_identifier = state_change_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find state that matches the current channel locksroots. ' f'chain_id:{raiden.chain.network_id} ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ', ) restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=channel_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state, ) skip_unlock = (restored_channel_state.partner_state.address == participant and gain.from_partner_locks == 0) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.partner_state, participant=our_address, partner=partner_address, ) if search_events: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=our_locksroot, recipient=partner_address, ) state_change_identifier = event_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find event that match current channel locksroots. ' f'chain_id:{raiden.chain.network_id} ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ', ) restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=canonical_identifier.channel_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state, ) skip_unlock = (restored_channel_state.our_state.address == participant and gain.from_our_locks == 0) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.our_state, participant=partner_address, partner=our_address, )
def test_get_event_with_balance_proof(): """ All events which contain a balance proof must be found by when querying the database. """ serializer = JSONSerializer storage = SerializedSQLiteStorage(":memory:", serializer) counter = itertools.count() lock_expired = SendLockExpired( recipient=factories.make_address(), message_identifier=next(counter), balance_proof=make_balance_proof_from_counter(counter), secrethash=sha3(factories.make_secret(next(counter))), ) locked_transfer = SendLockedTransfer( recipient=factories.make_address(), channel_identifier=factories.make_channel_identifier(), message_identifier=next(counter), transfer=make_transfer_from_counter(counter), ) balance_proof = SendBalanceProof( recipient=factories.make_address(), channel_identifier=factories.make_channel_identifier(), message_identifier=next(counter), payment_identifier=next(counter), token_address=factories.make_address(), secret=factories.make_secret(next(counter)), balance_proof=make_balance_proof_from_counter(counter), ) refund_transfer = SendRefundTransfer( recipient=factories.make_address(), channel_identifier=factories.make_channel_identifier(), message_identifier=next(counter), transfer=make_transfer_from_counter(counter), ) events_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (locked_transfer, locked_transfer.balance_proof), (balance_proof, balance_proof.balance_proof), (refund_transfer, refund_transfer.transfer.balance_proof), ] timestamp = datetime.utcnow().isoformat(timespec="milliseconds") state_change = "" for event, _ in events_balanceproofs: state_change_identifier = storage.write_state_change( state_change, timestamp) storage.write_events(state_change_identifier=state_change_identifier, events=[event], log_time=timestamp) for event, balance_proof in events_balanceproofs: event_record = get_event_with_balance_proof_by_balance_hash( storage=storage, canonical_identifier=balance_proof.canonical_identifier, balance_hash=balance_proof.balance_hash, ) assert event_record.data == event event_record = get_event_with_balance_proof_by_locksroot( storage=storage, canonical_identifier=balance_proof.canonical_identifier, recipient=event.recipient, locksroot=balance_proof.locksroot, ) assert event_record.data == event # Checking that balance proof attribute can be accessed for all events. # Issue https://github.com/raiden-network/raiden/issues/3179 assert event_record.data.balance_proof == event.balance_proof