> **کانکشنیجات** *(Connectionijat)* جزو مهمی از برگزاری ایونتهای کشوری مانند **المپیکفناوری پردیس** هستند که **اختلالات** یا **شنود** در آنها میتواند منجر به تبدیل شدن به یک **فاجعه واقعی** خواهد شد...
**آقای لطفی** *(Mr.Lotfi)* **اسکواد لید کانتست** کوئرا که همواره پس از برگزاری **مسابقات** و **هکاتونها** توسط کوئرا، **نظرسنجیهای مفصلی** را برای سنجیدن و بهبود هر چه بیشتر تمام جنبههای رویداد برگزار شده از شرکتکنندگان انجام میدهد، پس از دریافت نتایج نظرسنجی مسابقات برنامهنویسی و هوشمصنوعی سری اول #المپیکفناوری پردیس، به یک مشکل اساسی که کاربران زیادی به آن پرداخته بودند پی میبرد، مشکلات کانکشنیجات!
کاربران در نظرسنجیها به **نبود زیرساختی مطمئن** و **سریع** برای **برقراری کانکشنیجات** بین خودشان و طراحان و دستاندرکاران **سری رویدادهای المپیکفناوری پردیس** معترض بودند! از آنجایی که **آقای لطفی** مثل همیشه مشتاق جلب بیشتر رضایت شرکتکنندگان است، به **پیمانوش** *(Peymanoosh)* در **تیم فنی کوئرا،** ماموریت جدیدی داده تا برنامهای برای برقراری ارتباطات بهتر میان شرکتکنندگان و برگزارکنندگان سری جدید #المپیکفناوری پردیس با عنوان "فناوری کانکشنیجات" توسعه دهد.

*فناوری کانکشنیجات* که بر اساس ارسال **بستههای داده** *(Data Packet)* طوری طراحی شده است که برای جلوگیری از شنود ارتباطات بین طراحان و برگزارکنندگان توسط شرکتکنندگان عادی، بستههای **طعمه** *(Bait)* ایجاد کند که اطلاعات واقعی ندارند، از دو بخش **ارسالکننده پیام** *(Transmitter)* و **دریافتکننده پیام** *(Receiver)* تشکیل شده است. همچنین علی رغم دانش فنی فراوان پیامنوش، به دلیل وسعت بسیار زیاد پارک فناوری پردیس، بستهها ممکن است با [**ترتیب نامشخص** *(Jitter)*](https://en.wikipedia.org/wiki/Jitter) به مقصد برسند.
# **پروژهی اولیه**
برای دانلود **پروژهی اولیه** روی [این لینک](/contest/assignments/84123/download_problem_initial_project/305473/) کلیک کنید.
<details class="yellow">
<summary>**ساختار فایلها**</summary>
```
fanavari-connectionijat/
├── tests/
├ ├── __init__.py
├ └── test_sample.py
├── base.py
├── <mark class="orange" title="این فایل را تکمیل کنید">packet.py</mark>
├── <mark class="orange" title="این فایل را تکمیل کنید">receiver.py</mark>
└── <mark class="orange" title="این فایل را تکمیل کنید">transmitter.py</mark>
```
</details>
# **جزئیات پروژه**
**تیم فنی کوئرا** و به ویژه **پیمانوش** این روزها به دلیل المپیکفناوری، لود زیادی را تحمل میکنند؛ از این رو پیمانوش تنها **بعضی بخشهای فناوری کانکشنیجات** را توسعه داده و پیادهسازی باقی قسمتهای آن را بر عهده شما قرار داده است تا شما نیز در توسعه بخشی از زیرساخت ارتباطاتی المپیکفناوری سهیم باشید. شما در این سوال قرار است بخشهای زیر از **فناوری کانکشنیجات** را طراحی کنید:
1. یک [**دیتاکلاس (Data Class)**](https://docs.python.org/3/library/dataclasses.html) برای مدلسازی **بستهها** پیادهسازی کنید.
2. کلاسی تحت عنوان **فرستنده** پیادهسازی کنید تا پیامها را با فرمت بستههای مدلسازی شده ارسال کند؛ همچنین متدی برای اضافه کردن **بستههای تقلبی** اضافه کنید تا در مرحلهی تست پیمانوش از آن استفاده شود.
3. کلاسی تحت عنوان **گیرنده** پیادهسازی کنید تا با دریافت پیام، بستههای طعمه را **فیلتر** کند و با مرتبسازی **بستههای واقعی** بر اساس شماره توالی، **محتوای پیام اولیه** را بازسازی نمایید.
<details class="red">
<summary>**پیادهسازی فایل** `base.py`</summary>
این فایل شامل **کلاس پایهای** به نام `Base` است که یک تابع کمکی مهم برای انجام عملیات هش *(Hash)* در اختیار **سایر کلاسها** (`Transmitter` و `Receiver`) قرار میدهد.
محتوای این فایل از قبل به شکل زیر پیادهسازی شده است و در **فایل پروژه اولیه** قرار گرفته است:
```python base.py python
import hashlib
class Base:
def _hash_function(self, data: str, key: str) -> str:
return hashlib.sha256((data + key).encode()).hexdigest()
```
</details>
<details class="blue">
<summary>**پیادهسازی فایل** `packet.py`</summary>
در این فایل باید **دیتاکلاس** `Packet` را پیادهسازی کنید. هر بستهی دادهای که بین **فرستنده** و **گیرنده** در **فناوری کانکشنیجات** منتقل میشود، با این ساختار مدلسازی شده است که فیلدهای آن به شکل زیر میباشد:
| نام فیلد | نوع داده | توضیح |
| ----------------- | -------- | ------------------------------- |
| `sequence_number` | `int` | مشخصکنندهی جایگاه بسته در پیام اصلی (برای مرتبسازی) |
| `data` | `str` | محتوای واقعی بسته (یک بخش از پیام اصلی یا طعمه) |
| `hashed_data` | `str` | هش تولید شده از `data` با استفاده از کلید عمومی و تابع موجود در کلاس `Base` |
+ **توجه کنید** که شما باید **فیلدهای جدول بالا** را **دقیقا** با **نام و نوع** گفته شده پیادهسازی کنید، در غیر این صورت در سیستم داوری نمرهای به شما تعلق **نخواهد گرفت.**
+ همچنین **توجه داشته** باشید که شما **مجازید** تا **هر تعداد فیلد دلخواه دیگری** را نیز برای پیادهسازی ادامه برنامه، در این کلاس معرفی کنید.
## **پیادهسازی متد** `is_valid`
```python packet.py python
def is_valid(self, hash_func: Callable, key: str) -> bool:
pass
```
+ **خروجی** این متد از نوع **بولین** خواهد بود؛ در صورت برابر بودن **مقدار** `hashed_data` بستهی موردنظر با مقدار **خروجی تابع** `hash_func` خروجی برابر با `True` و در غیر اینصورت برابر با `False` خواهد بود.
**نکته:** **تابعی** که در **پارامتر** `hash_func` به متد موردنظر پاس داده میشود، دارای **دو ورودی از نوع رشته** خواهد بود که **ورودی اول رشته** `data` بستهی موردنظر و **ورودی دوم** `key` میباشد که **کلیدی** است که با آن `data` **هش** شده است.
نمونهای از این تابع:
```python
def example_hash_func(data: str, key: str) -> str:
return f"hashed_{key}"
```
+ **توجه داشته باشید** که عملکرد هش کردن این تابع در تستهای سوال **متغیر** خواهد بود و همیشه عملیات هش کردن به شکل بالا انجام **نخواهد شد. این مورد صرفا یک نمونه برای درک بهتر سوال است.**
</details>
<details class="green">
<summary>**پیادهسازی فایل** `transmitter.py`</summary>
در این فایل باید **کلاس** `Transmitter` را پیادهسازی کنید. این کلاس **پیام ورودی** را به تکههای کوچک تقسیم میکند، برای هر تکه یک **بستهی واقعی** با هش معتبر میسازد، سپس به **تعداد مشخصی** بستهی **طعمه** اضافه میکند، **ترتیب** همهی بستهها را **بههم میزند** و آنها را به گیرنده ارسال میکند. این کلاس از `Base` **ارثبری** میکند و برای **ساخت هش** از متد کمکی داخل این کلاس استفاده میکند.
## **پیادهسازی سازندهی کلاس**
در **متد سازندهی کلاس** `Transmitter`، کلید مشترک که برای تولید هش بستههای واقعی استفاده میشود به عنوان ورودی دریافت شده و در **متغیر داخلی** `self._key` ذخیره میگردد. علاوه بر این، هنگام ایجاد شیء میتوان پارامترهای دیگری نیز مشخص کرد. **پارامتر** `self.bait_count` **تعداد بستههای طعمهای** را تعیین میکند که در ادامه ساخته خواهند شد، همچنین **پارامتر** `self.chunk_size` اندازهی هر برش از پیام را مشخص میکند تا پیام اصلی در **فرآیند تکهتکهسازی** به قطعات مناسب تقسیم شود. این مقادیر علاوه بر کلید ورودی، امکان مدیریت دقیقتر و انعطافپذیری بیشتر در پردازش و اعتبارسنجی دادهها را فراهم میآورند.
```python transmitter.py python
def __init__(key: str, bait_count: int = 2, chunk_size: int = 3):
pass
```
## **پیادهسازی متد** `_add_bait_packets`
این متد به **تعداد** `bait_count` **بستههای طعمه** میسازد و آنها را به **لیست** `packets` ارسال شده اضافه میکند. **توجه داشته باشید که محتوای بستههای طعمه اهمیتی ندارد.**
```python
def _add_bait_packets(self, packets: list) -> None:
return None
```
## **پیادهسازی متد** `_prepare_packets`
این متد ابتدا پیام را به **تکههایی** با **طول** `chunk_size` تقسیم میکند (تکهی آخر ممکن است از این مقدار کمتر باشد): سپس برای هر تکه، هش معتبر را با توابع موجود تولید میکند و یک نمونه از دیتاکلاس `Packet` با دادههای فعلی ایجاد میکند که شمارهی هر بسته نیز به **صورت متوالی** با شروع از عدد `1` شمارهگذاری میشود.
سپس با استفاده از متد `_add_bait_packets`، طعمهها را به لیست بستهها اضافه میکند و در پایان لیست نهایی را بُر میزند *(Shuffle)* و آن را برمیگرداند *(*`return` *میکند).*
```python transmitter.py python
def _prepare_packets(self, message: str) -> list[Packet]:
pass
```
## **پیادهسازی متد** `transmit`
این **متد** باید پیام موردنظر را به گیرندهی مربوطه ارسال میکند؛ برای اینکار ابتدا با **متد** `_prepare_packets(message)` بستهها ساخته شود و سپس آنها را به **متد** `receive` گیرنده ارسال کند؛ درنهایت همان بستهها را به عنوان خروجی متد برگردانده شود.
```python transmitter.py python
def transmit(self, message: str, receiver: Receiver) -> list[Packet]:
pass
```
</details>
<details class="yellow">
<summary>**پیادهسازی فایل** `receiver.py`</summary>
در این **فایل،** شما باید **کلاسی** به نام `Receiver` پیادهسازی کنید که بتواند بستههای دریافتی از فرستنده را بررسی کرده؛ **بستههای تقلبی** *(Bait)* را شناسایی و فیلتر کند و در نهایت پیام اصلی را بازسازی کند. **این کلاس دارای سه متد زیر است که باید هر یک را به صورت خواسته شده پیادهسازی کنید.**
## **پیادهسازی سازندهی کلاس**
این **متد** هنگام ساخته شدن شیء از **کلاس** `Receiver` فراخوانی میشود و **کلید عمومی** (رشتهی `pub_key`) را به عنوان ورودی دریافت میکند و در پارامتر `self._key` قرار میدهد. از این کلید جلوتر در اعتبارسنجی هشها استفاده خواهد شد. **توجه داشته باشید که شما میتوانید متغیرهای کمکی دیگری نیز در متد ایجاد کنید.**
```python receiver.py python
def __init__(self, key: str):
pass
```
## **پیادهسازی متد** `receive`
این **متد،** یک لیست از بستهها (`packets`) که هر عضو آن از نوع `Packet` است را دریافت میکند. باید مشخص شود که هر بسته معتبر است یا خیر و در صورت معتبر بودن، در یک لیست دیگر ذخیره شود. برای بررسی **معتبر بودن** هر بسته با استفاده از **متد**`is_valid` بستهی موردنظر استفاده شود. فقط بستههایی که **فیلد** `hased_data` آنها با مقدار **هش** `data` با استفاده از کلید برابر است را ذخیره کند و **بستههای نامعتبر** نادیده گرفته شوند.
```python receiver.py python
def receive(self, packets: list[Packet]) -> None:
pass
```
## **پیادهسازی متد** `get_message`
این متد، پس از با فراخوانی **متد قبلی** *(دریافت تمام بستهها و حذف بستههای طعمه)* باید با **مرتبسازی لیست بستهها،** رشتهی حاوی پیام اصلی را بازسازی کند. در صورتی که دو بستهی معتبر با شمارهی توالی برابر وجود داشته باشند باید **به ترتیب ورودی در پیام خروجی نمایش داده شوند.**
```python receiver.py python
def get_message(self) -> str:
pass
```
</details>
## **مثالها**
### **نمونهی مثال ۱**
```python main.py python
from transmitter import Transmitter
from receiver import Receiver
transmitter = Transmitter(key="k", bait_count=2, chunk_size=3)
receiver = Receiver("k")
packets = transmitter.transmit("hello world", receiver)
print(packets)
# ["hel", "low", "orl", "d"]
message = receiver.get_message()
print(message)
# hello world
```
+ در این مثال، پیام به **تکههای** `["hel", "low", "orl", "d"]` تقسیم میشود و سپس **سه بستهی طعمه** به لیست بستهها افزوده و کل لیست درهمریزی میشود. سپس با استفاده از **متد** `transmit` به گیرندهی مربوطه ارسال میشود؛ در نهایت با **فراخوانی متد** `get_message` در گیرندهی موردنظر، **پیام اولیه** (`hello world`) چاپ میشود.
### **نمونهی مثال ۲**
```python main.py python
from transmitter import Transmitter
from receiver import Receiver
transmitter = Transmitter(key="s3cr3t", bait_count=3, chunk_size=4)
receiver = Receiver("s3cr3t")
packets = transmitter.transmit("abcdefghijk", receiver)
# ["abcd", "efgh", "ijk"]
message = receiver.get_message()
# abcdefghijk
```
* در این مثال، پیام به **تکههای** `["abcd", "efgh", "ijk"]` تقسیم میشود، سپس **سه بستهی طعمه** به لیست بستهها افزوده و کل لیست درهمریزی میشود. سپس با استفاده از **متد** `transmit` به گیرندهی مربوطه ارسال میگردد؛ در نهایت با **فراخوانی متد** `get_message` در گیرنده، **پیام اولیه** (`abcdefghijk`) بازسازی و چاپ میشود.
# **آنچه باید آپلود کنید**
+ **توجه**: پس از اعمال تغییرات، کل پروژه را _Zip_ کرده و آپلود کنید. **همانند پروژه اولیه در فایل زیپ شده نباید کد در پوشهی دیگری قرار بگیرد در غیر این صورت سیستم داوری فایل را شناسایی نکرده و نمرهای دریافت نخواهید کرد.**
+ **توجه:** تنها فایلهایی که در **ساختار پروژه** مشخص شدهاند، در سیستم داوری **مورد پذیرش** قرار خواهد گرفت و سایر تغییرات در سایر فایلها **بیتاثیر** خواهند بود.
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.