def submit_send(
            self, *,
            payload: dict,
            node_id_or_blinded_path: bytes,
            key: bytes = None) -> 'Task':
        """Add onion message to queue for sending. Queued onion message payloads
           are supplied with a path_id and a reply_path to determine which request
           corresponds with arriving replies.

           If caller has provided 'reply_path' in payload, caller should also provide associating key.

           :return: returns awaitable task"""
        if not key:
            key = os.urandom(8)
        assert type(key) is bytes and len(key) >= 8

        self.logger.debug(f'submit_send {key=} {payload=} {node_id_or_blinded_path=}')

        req = OnionMessageManager.Request(
            future=asyncio.Future(),
            payload=payload,
            node_id_or_blinded_path=node_id_or_blinded_path
        )
        with self.pending_lock:
            if key in self.pending:
                raise Exception(f'{key=} already exists!')
            self.pending[key] = req

        # tuple = (when to process, when it expires, key)
        expires = now() + self.REQUEST_REPLY_TIMEOUT
        queueitem = (now(), expires, key)
        self.send_queue.put_nowait(queueitem)
        task = asyncio.create_task(self._wait_task(key, req.future))
        return task
Add onion message to queue for sending. Queued onion message payloads
           are supplied with a path_id and a reply_path to determine which request
           corresponds with arriving replies.

           If caller has provided 'reply_path' in payload, caller should also provide associating key.

           :return: returns awaitable task
electrum/onion_message.py