def events_for_balanceproof(transfers_pair, block_number): """ Send the balance proof to nodes that know the secret. """ events = list() for pair in reversed(transfers_pair): payee_knows_secret = pair.payee_state in STATE_SECRET_KNOWN payee_payed = pair.payee_state in STATE_TRANSFER_PAID payee_channel_open = pair.payee_route.state == CHANNEL_STATE_OPENED # XXX: All nodes must close the channel and withdraw on-chain if the # lock is nearing it's expiration block, what should be the strategy # for sending a balance proof to a node that knowns the secret but has # not gone on-chain while near the expiration? (The problem is how to # define the unsafe region, since that is a local configuration) lock_valid = is_lock_valid(pair.payee_transfer, block_number) if payee_channel_open and payee_knows_secret and not payee_payed and lock_valid: pair.payee_state = 'payee_balance_proof' balance_proof = SendBalanceProof( pair.payee_transfer.identifier, pair.payee_route.channel_address, pair.payee_transfer.token, pair.payee_route.node_address, pair.payee_transfer.secret, ) events.append(balance_proof) return events
def handle_secretreveal(state, state_change): """ Send a balance proof to the next hop with the current mediated transfer lock removed and the balance updated. """ if state_change.sender == state.route.node_address: # next hop learned the secret, unlock the token locally and send the # withdraw message to next hop transfer = state.transfer unlock_lock = SendBalanceProof( transfer.identifier, state.route.channel_address, transfer.token, state.route.node_address, transfer.secret, ) transfer_success = EventTransferSentSuccess( transfer.identifier, transfer.amount, transfer.target, ) unlock_success = EventUnlockSuccess( transfer.identifier, transfer.hashlock, ) iteration = TransitionResult(None, [unlock_lock, transfer_success, unlock_success]) else: iteration = TransitionResult(state, list()) return iteration
def create_unlock( channel_state: NettingChannelState, message_identifier: typing.MessageID, payment_identifier: typing.PaymentID, secret: typing.Secret, lock: HashTimeLockState, ) -> SendUnlockAndMerkleTree: our_state = channel_state.our_state msg = 'caller must make sure the lock is known' assert is_lock_pending(our_state, lock.secrethash), msg msg = 'caller must make sure the channel is open' assert get_status(channel_state) == CHANNEL_STATE_OPENED, msg our_balance_proof = our_state.balance_proof if our_balance_proof: transferred_amount = lock.amount + our_balance_proof.transferred_amount else: transferred_amount = lock.amount merkletree = compute_merkletree_without( our_state.merkletree, lock.lockhash, ) locksroot = merkleroot(merkletree) token = channel_state.token_address nonce = get_next_nonce(our_state) recipient = channel_state.partner_state.address locked_amount = get_amount_locked( our_state) - lock.amount # the lock is still registered balance_proof = BalanceProofUnsignedState( nonce, transferred_amount, locked_amount, locksroot, channel_state.token_network_identifier, channel_state.identifier, ) queue_name = channel_state.identifier unlock_lock = SendBalanceProof( recipient, queue_name, message_identifier, payment_identifier, token, secret, balance_proof, ) return unlock_lock, merkletree
def create_unlock(channel_state, message_identifier, payment_identifier, secret, lock): msg = 'caller must make sure the lock is known' assert is_lock_pending(channel_state.our_state, lock.secrethash), msg msg = 'caller must make sure the channel is open' assert get_status(channel_state) == CHANNEL_STATE_OPENED, msg our_balance_proof = channel_state.our_state.balance_proof if our_balance_proof: transferred_amount = lock.amount + our_balance_proof.transferred_amount else: transferred_amount = lock.amount merkletree = compute_merkletree_without( channel_state.our_state.merkletree, lock.lockhash, ) locksroot = merkleroot(merkletree) token = channel_state.token_address nonce = get_next_nonce(channel_state.our_state) recipient = channel_state.partner_state.address balance_proof = BalanceProofUnsignedState( nonce, transferred_amount, locksroot, channel_state.identifier, ) queue_name = channel_state.identifier unlock_lock = SendBalanceProof( recipient, queue_name, message_identifier, payment_identifier, token, secret, balance_proof, ) return unlock_lock, merkletree
def create_unlock(channel_state, identifier, secret, lock): msg = 'caller must make sure the lock is known' assert is_known(channel_state.our_state, lock.hashlock), msg our_balance_proof = channel_state.our_state.balance_proof if our_balance_proof: transferred_amount = lock.amount + our_balance_proof.transferred_amount else: transferred_amount = lock.amount merkletree = compute_merkletree_without( channel_state.our_state.merkletree, lock.lockhash, ) locksroot = merkleroot(merkletree) token = channel_state.token_address nonce = get_next_nonce(channel_state.our_state) recipient = channel_state.partner_state.address balance_proof = BalanceProofUnsignedState( nonce, transferred_amount, locksroot, channel_state.identifier, ) unlock_lock = SendBalanceProof( identifier, token, recipient, secret, balance_proof, ) return unlock_lock, merkletree
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 = SQLiteStorage(':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( storage=storage, chain_id=balance_proof.chain_id, token_network_identifier=balance_proof.token_network_identifier, channel_identifier=balance_proof.channel_identifier, balance_hash=balance_proof.balance_hash, ) assert event_record.data == event
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(1) partner_address = factories.make_address() balance_proof = make_balance_proof_from_counter(counter) lock_expired = SendLockExpired( recipient=partner_address, message_identifier=MessageID(next(counter)), balance_proof=balance_proof, secrethash=factories.make_secret_hash(next(counter)), canonical_identifier=balance_proof.canonical_identifier, ) locked_transfer = SendLockedTransfer( recipient=partner_address, message_identifier=MessageID(next(counter)), transfer=make_transfer_from_counter(counter), canonical_identifier=factories.make_canonical_identifier(), ) send_balance_proof = SendBalanceProof( recipient=partner_address, message_identifier=MessageID(next(counter)), payment_identifier=factories.make_payment_id(), token_address=factories.make_token_address(), secret=factories.make_secret(next(counter)), balance_proof=make_balance_proof_from_counter(counter), canonical_identifier=factories.make_canonical_identifier(), ) refund_transfer = SendRefundTransfer( recipient=partner_address, message_identifier=MessageID(next(counter)), transfer=make_transfer_from_counter(counter), canonical_identifier=factories.make_canonical_identifier(), ) events_balanceproofs = [ (lock_expired, lock_expired.balance_proof), (locked_transfer, locked_transfer.balance_proof), (send_balance_proof, send_balance_proof.balance_proof), (refund_transfer, refund_transfer.transfer.balance_proof), ] state_change = Block(BlockNumber(1), BlockGasLimit(1), factories.make_block_hash()) for event, _ in events_balanceproofs: state_change_identifiers = storage.write_state_changes([state_change]) storage.write_events(events=[(state_change_identifiers[0], event)]) 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, recipient=partner_address, ) assert event_record 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 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 storage.close()
def test_pfs_global_messages( matrix_transports, monkeypatch, ): """ Test that `update_pfs` from `RaidenEventHandler` sends balance proof updates to the global PATH_FINDING_BROADCASTING_ROOM room on Send($BalanceProof)* events, i.e. events, that send a new balance proof to the channel partner. """ transport = matrix_transports[0] transport._client.api.retry_timeout = 0 transport._send_raw = MagicMock() raiden_service = MockRaidenService(None) transport.start( raiden_service, raiden_service.message_handler, None, ) pfs_room_name = make_room_alias(transport.network_id, PATH_FINDING_BROADCASTING_ROOM) pfs_room = transport._global_rooms.get(pfs_room_name) assert isinstance(pfs_room, Room) pfs_room.send_text = MagicMock(spec=pfs_room.send_text) raiden_service.transport = transport transport.log = MagicMock() # create mock events that should trigger a send lock = make_lock() hash_time_lock = HashTimeLockState(lock.amount, lock.expiration, lock.secrethash) def make_unsigned_balance_proof(nonce): return BalanceProofUnsignedState.from_dict( make_balance_proof(nonce=nonce, signer=LocalSigner(HOP1_KEY), amount=1).to_dict(), ) transfer1 = LockedTransferUnsignedState( balance_proof=make_unsigned_balance_proof(nonce=1), payment_identifier=1, token=b'1', lock=hash_time_lock, target=HOP1, initiator=HOP1, ) transfer2 = LockedTransferUnsignedState( balance_proof=make_unsigned_balance_proof(nonce=2), payment_identifier=1, token=b'1', lock=hash_time_lock, target=HOP1, initiator=HOP1, ) send_balance_proof_events = [ SendLockedTransfer(HOP1, 1, 1, transfer1), SendRefundTransfer(HOP1, 1, 1, transfer2), SendBalanceProof(HOP1, 1, 1, 1, b'1', b'x' * 32, make_unsigned_balance_proof(nonce=3)), SendLockExpired(HOP1, 1, make_unsigned_balance_proof(nonce=4), b'x' * 32), ] for num, event in enumerate(send_balance_proof_events): assert event.balance_proof.nonce == num + 1 # make sure we cover all configured event types assert all(event in [type(event) for event in send_balance_proof_events] for event in SEND_BALANCE_PROOF_EVENTS) event_handler = raiden_event_handler.RaidenEventHandler() # let our mock objects pass validation channelstate_mock = Mock() channelstate_mock.reveal_timeout = 1 monkeypatch.setattr( raiden_event_handler, 'get_channelstate_by_token_network_and_partner', lambda *args, **kwargs: channelstate_mock, ) monkeypatch.setattr(raiden_event_handler, 'state_from_raiden', lambda *args, **kwargs: 1) monkeypatch.setattr(event_handler, 'handle_send_lockedtransfer', lambda *args, **kwargs: 1) monkeypatch.setattr(event_handler, 'handle_send_refundtransfer', lambda *args, **kwargs: 1) # handle the events for event in send_balance_proof_events: event_handler.on_raiden_event( raiden_service, event, ) gevent.idle() # ensure all events triggered a send for their respective balance_proof # matrix transport may concatenate multiple messages send in one interval assert pfs_room.send_text.call_count >= 1 concatenated_call_args = ' '.join( str(arg) for arg in pfs_room.send_text.call_args_list) assert all(f'"nonce": {i + 1}' in concatenated_call_args for i in range(len(SEND_BALANCE_PROOF_EVENTS))) transport.stop() transport.get()
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 # 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