Как сделать анонимную отправку очков в python?

Пытаюсь реализовать анонимный перевод из одного счета в другой. В план, который я придумал, чтобы добиться анонимности.:

План:

  1. Создать две переменные balance и anonim.

  2. Инициализировать переменную balance для хранения очков.

  3. Если клиент хочет перевести другу свои очки, то:

    ◦Разделить его сумму очков(которое он отправил) на два рандомнох числа.

    ◦Передать каждое полученное число двум случайным клиентам в переменную anonim.

  4. Повторить данный шаг 3 раза:

    ◦Взять у клиентов, которые получили очки, разделить их очки на два случайных числа и отправить другим двум случайным клиентам в переменную "anonim".

  5. Просуммировать значения в переменной anonim всех конечных случайных клиентов.

  6. Отправить полученную сумму на счет друга.

Кратко:

  • Алгоритм оперирует по следующей схеме: сумма перевода разбивается на два случайных числа и направляется на скрытые счета двух случайно выбранных пользователей. Процедура повторяется дважды для усиления анонимности. В итоге, суммы передаются получателям в случайном порядке, что обеспечивает дополнительный уровень конфиденциальности.

Вот рисунок, как это должно все выглядит:

введите сюда описание изображения

Проблемы:

Главным вызовом, с которым я столкнулся, является неспособность эффективно реализовать концепцию на сервере. При работе с сервером возникают проблемы — либо он перестает отвечать, либо выводит ошибку и перестает функционировать. В теории, алгоритм должен брать случайных клиентов из переменной clients = {} и включать их в процесс. Однако, как я уже упоминал, возникают ошибки или просто отсутствует ответ от сервера.
Вот примерно, что я пытаюсь получить:

import random

x = int(input("Введите сумму очков, которую хотите перевести другу: "))

while True:
    anonim1 = random.randint(1, x)
    anonim2 = random.randint(1, x)
    anonim3 = random.randint(1, anonim1)
    anonim4 = random.randint(1, anonim1)
    anonim5 = random.randint(1, anonim2)
    anonim6 = random.randint(1, anonim2)

    if anonim1 + anonim2 == x and anonim3 + anonim4 + anonim5 + anonim6 == x:
        break

print(f"Значение переменной anonim у первого клиента: {anonim1}")
print(f"Значение переменной anonim у второго клиента: {anonim2}")
print(f"Из переменной anonim у первого клиента: {anonim1}, появилось {anonim3} и {anonim4} и отправилось другим клиентам")
print(f"Из переменной anonim у второго клиента: {anonim2}, появилось {anonim5} и {anonim6} и отправилось другим клиентам")

balancefreand = anonim3 + anonim4 + anonim5 + anonim6
print(f"Баланс друга теперь: {balancefreand}")

(Данный код никак не связан не с сервером, не с клиентом. Я тут просто показал, как должна выглядеть моя задумка)

Вот пример, как это должно выглядеть по моей задумки:

Пользователь вводит команду для перевода очков:

введите сюда описание изображения

Обмен очков: загрузка данных на сервер:

введите сюда описание изображения

Буду очень признателен за помощь в реализации моей идеи и оптимизации программы. Внизу привожу свой код, в котором еще нет функционала анонимных переводов, так как мне не совсем ясен лучший способ воплотить мою задумку. Заранее благодарю всех, кто окажет мне помощь в этом вопросе!

Изменение кода сервера и клиента:

После тщательного обдумывания советов, я принял решение полностью переделать свой код, внедрив механизм буферизации для оптимизации работы. Несмотря на то, что не все прошло гладко, прошу вас смотреть на это снисходительно. Вместо стандартных модулей socket и threading, я воспользовался возможностями модуля asyncio. Я надеюсь, что все выполнено правильно, но буду благодарен за любые замечания и советы, высказанные в комментариях.

Сервер и клиент:

Сервер:

import asyncio
import random

connected_users = {}
user_balances = {}

async def handle_client(reader, writer):
    user_id = random.randint(100000, 999999)
    connected_users[user_id] = writer
    user_balances[user_id] = 100
    writer.write(str(user_id).encode())
    print(f"Пользователь с айди {user_id} и балансом 100 подключился. Онлайн: {len(connected_users)}")

    try:
        while True:
            data = await reader.read(100)
            message = data.decode().strip()
            if message.startswith('/trade '):
                parts = message.split()
                if len(parts) == 3:
                    trade_id = int(parts[1])
                    amount = int(parts[2])
                    if trade_id in connected_users and amount <= user_balances[user_id]:
                        user_balances[user_id] -= amount
                        user_balances[trade_id] += amount
                        writer.write(f"Успешно обменено {amount} очков с пользователем {trade_id}\n".encode())
                        connected_users[trade_id].write(f"Получено {amount} очков от пользователя {user_id}\n".encode())
                    else:
                        writer.write("Ошибка при обмене\n".encode())
            elif message == '/balance':
                writer.write(f"Ваш текущий баланс: {user_balances[user_id]} очков\n".encode())
            elif message == '/help':
                writer.write("Доступные команды:\n/trade [айди] [сумма] - выполнить обмен очков\n/balance - проверить баланс\n/help - отобразить все доступные команды и инструкцию для команды /trade\n/exit - выйти из сервера\n".encode())
            elif message == '/exit':
                writer.write("До свидания! Соединение будет закрыто\n".encode())
                break
            await asyncio.sleep(0.1)
    except asyncio.CancelledError:
        pass
    finally:
        del connected_users[user_id]
        del user_balances[user_id]
        print(f"Пользователь с айди {user_id} отключился. Онлайн: {len(connected_users)}")
        writer.close()

async def main():
    server = await asyncio.start_server(
        handle_client, '127.0.0.1', 8888)

    addr = server.sockets[0].getsockname()
    print(f'Server started on {addr}')

    async with server:
        await server.serve_forever()

asyncio.run(main())


Клиент:

import asyncio

async def handle_client(reader, writer):
    data = await reader.read(100)
    user_id = int(data.decode())
    print(f"Вы получили айди: {user_id}")

    async def receive_messages():
        while True:
            data = await reader.read(100)
            if not data:
                break
            print(data.decode(), end='')

    task = asyncio.create_task(receive_messages())

    try:
        while True:
            command = input("Введите команду: ")

            if command.lower() == '/exit':
                writer.write('/exit'.encode())
                await writer.drain()
                break
            elif command.lower() == '/balance':
                writer.write('/balance'.encode())
                await writer.drain()
                response_data = await reader.read(1024)
                print(response_data.decode())
            elif command == '/help':
                writer.write('/help'.encode())
                await writer.drain()
                response_data = await reader.read(1024)
                print("Доступные команды:\n", response_data.decode())
            elif command.startswith('/trade'):
                recipient_id, amount = command.split()[1:]
                trade_command = f"/trade {recipient_id} {amount}"
                writer.write(trade_command.encode())
                await writer.drain()
                response_data = await reader.read(1024)
                print(response_data.decode())
            else:
                print("Неверная команда")
    except asyncio.CancelledError:
        pass
    finally:
        task.cancel()
        writer.close()
        await writer.wait_closed()

async def main():
    reader, writer = await asyncio.open_connection('127.0.0.1', 8888)

    await writer.drain()  # Отправляем пустое сообщение для получения айди
    await handle_client(reader, writer)

asyncio.run(main())

Ответы (1 шт):

Автор решения: Mixadyt

Прочитал ваш код, заметил несколько проблем:

  1. Id может стать не уникальным (id_value = str(random.randint(100000, 999999))), поэтому стоит проверить, если id_value in clients.keys();
  2. Где команда "/balance", вы отправляете текст "Ваши очки: ..." с сервера, а клиент подставляет этот ответ. В итоге получится: "Ваши очки: Ваши очки: ...";
  3. Не совсем проблема конечно, но можно прямо на клиенте развернуть команду "/help"; Это всё что я заметил на данный момент.
→ Ссылка