تم إنشاء هذه الترجمة باستخدام التعلم الآلي وقد لا تكون دقيقة بنسبة 100%. عرض النسخة الإنجليزية

امتداد سريع (BEP 6) مسموح به سريع مع هوية نظير بهاش الوجهة

Proposal 172
مسودة
Author dr|z3d
Created 2026-08-10
Last Updated 2026-08-10

نظرة عامة

تحزم BEP 6 (الامتداد السريع) خمس ميزات: امتلاك الكل / عدم امتلاك شيء، رفض الطلبات، الاقتراحات، والسريع المسموح. إن بروتوكول النقل — بت التفاوض، ومعرفات الرسائل، ومعاني الاختناق — لا يعتمد على نمط النقل وهو يعمل كما هو عبر البث في I2P. الجزء الوحيد من BEP 6 الذي لا يمكن ربطه مباشرةً بـ I2P هو إنشاء مجموعة السريع المسموح، لأن تعريفه يعتمد على عنوان IPv4 للنقطة الطرفية. النقاط الطرفية في I2P ليس لديها عناوين IP؛ بل تُعرف بواسطة هاشات وجهة بطول 32 بايتًا.

يُوحّد هذا الاقتراح إنشاء مجموعة “Allowed Fast” الأصلية في I2P بحيث تُولّد جميع عميلات التورنت في I2P نفس مجموعة “Allowed Fast” لكل نظير وتورنت، ما يجعل الميزة مفيدة (وقابلة للتحقق) عبر مختلف التنفيذات.

الدافع

يحتاج الأقران الجدد إلى استلام القطع الأولى قبل أن تتمكن آلية BitTorrent المتبادلة (tit-for-tat) من التسارع. في شبكة I2P تكون هذه العملية أبطأ مقارنة بالشبكة العادية (clearnet): حيث تعبر إعدادات الاتصال وتوصيل القطع عدة قفزات عبر أنفاق عالية التأخر، ما يجعل الفترة الزمنية بين الاتصال وأول فتح متبادل للقطع (unchoke) أطول. يعالج خيار “Allowed Fast” هذه المشكلة مباشرة — إذ يُسمح للنقطة الطرفية الجديدة بإرسال عدد صغير من القطع حتى أثناء الحظر (choked)، وبالتالي تتلقى البيانات فورًا وتستطيع البدء في المبادلة بشكل أسرع.

تحسب BEP 6 المرجعية المجموعة السريعة المسموحة من عنوان IPv4 للطرف المتصل لضمان أن المرسل يمكنه اختيار قطع فريدة بالنسبة إلى المستقبل (أي لا يمكن لمستخدم واحد يمتلك العديد من عناوين IP أن يجمع العديد من المجموعات). في شبكة I2P، يؤدي تجزئة وجهة الطرف نفس الدور التقييدي، وهي متاحة لكلا طرفي كل اتصال، ما يجعل المجموعة محددة وقابلة للتحقق محليًا — وهي ميزة لا يمكن أن يوفرها النظام القائم على عناوين IP.

تعديلات على BEP 6

يتم اعتماد مفاوضات الامتداد السريع وجميع أنواع الرسائل الأربعة دون تغيير:

  • التفاوض: البت الأقل أهمية الثالث في البايت المحفوظ الأخير، reserved[7] |= 0x04، من كلا الطرفين
  • تم التحقق من الكل <len=0x0001><op=0x0E>، لم يتم التحقق من أي شيء <len=0x0001><op=0x0F>
  • اقتراح جزء <len=0x0005><op=0x0D><index>
  • رفض الطلب <len=0x000D><op=0x10><index><begin><length>
  • السماح بالتنزيل السريع <len=0x0005><op=0x11><index>
  • كل طلب يؤدي إلى استجابة واحدة بالضبط (جزء أو رفض)؛ لم يعد الحظر يرفض تلقائيًا الطلبات المعلقة

الانحراف الوحيد هو في إنشاء مجموعة “السريع المسموح”، حيث يتم استبدال بايتات عنوان IP ببايتات هاش وجهة الند.

الانحراف: بايتات الهاش بدلاً من عنوان IP المقنّع

راجع BEP 6، الخطوة (1):

x = 0xFFFFFF00 & ip

هذا يستخدم ثلاثة بايتات من عنوان IPv4 للطرف المتصل ويُصفر البايت الرابع. هذه طريقة تقديرية للشبكة الفرعية: يجب ألا يحصل المستخدمون الذين يستطيعون الحصول على عناوين IP متعددة ضمن نفس الشبكة /24 على مجموعات سريعة مسموحة متعددة.

يقوم إصدار I2P الخاص بنا باستبدال هذا بأول أربع بايتات من هاش الوجهة البالغ 32 بايتًا للطرف المتصل:

x = first 4 bytes of peer destination hash

التمييز عن التنفيذ المرجعي:

“هذا هو 3 بايتات من عنوان IP متبوعة بصفر. أنت 4 بايتات من الهاش. وهو يختلف عن BEP 6 لأنها لا تحتوي على عنوان IP، ولا تقوم بتصفير البايت الرابع.”

كلا طرفي اتصال I2P يعرفان بالفعل كتلة تحديد موقع الطرف (وهي العنوان الذي تم الاتصال به/منه)، وبالتالي لا يتطلب هذا أي تبادل إضافي، ولا اكتشاف NAT، ولا كشف عنوان IP الخارجي — ولا أي من هذه الأمور موجود في I2P.

خوارزمية التوليد السريع المسموح بها

ليكن hash هو الهاش 32 بايت للمستلم، وinfohash هو هاش المعلومات 20 بايت للتيار، وsz هو عدد القطع في التورنت، وk هو العدد النهائي للقطع في مجموعة السرعة المسموحة (10، كما في BEP 6)، وa هي المجموعة الناتجة:

x = hash[0:4]  ++  infohash        (1)
while |a| < k:
    x = SHA1(x)                    (2)
    for i in [0:5] and |a| < k:    (3)
        y = x[i*4 : i*4+4]         (4)
        index = y % sz             (5)
        if index not in a:         (6)
            add index to a         (7)

ملاحظات:

  • 4 بايتات من تجزئة الوجهة تستبدل 3 بايتات IP معطّلة. جميع البايتات الأربع تحمل إنتروبيا التجزئة؛ ولا تُفرغ أي منها.
  • كما في BEP 6، فإن سلسلة SHA1 تنتج تسلسلاً شبه عشوائي طويل يُقسّم إلى فهارس قطع؛ حيث إن k = 10 يتطابق مع الإعداد الافتراضي المرجعي.
  • رسالة “Allowed Fast” هي توجيهية: على المستقبل ألا يفسرها على أنها تعني أن المرسل يمتلك القطعة — بل فقط أن المرسل سيُقدِّم تلك القطعة أثناء الاختناق.

الفوائد

المجالالفائدة
زمن البدءيستخدم الطرف الجديد القطع الأولى أثناء الحظر، مما يقلل من فترة الزيادة التدريجية (tit-for-tat ramp) التي تكون أبطأ عبر الاتصالات متعددة الوصلات في شبكة I2P
التنبؤيةالمجموعة هي دالة نقية (pure function) من الـ destination hash + infohash، وبالتالي فإن أي تنفيذ يحسب نفس المجموعة — على عكس BEP 6 المعتمد على IP، حيث قد يختلف رأي المرسل حول عنوان المستقبل (بسبب NAT)
إمكانية التحققيعرف الطرف المستقبل الـ destination hash الخاص به ويمكنه إعادة حساب وتحقق من صحة المجموعة محليًا، مما يمكنه من كشف المرسلين غير المتعاونين
عدم استخدام آليات IPلا حاجة لتجاوز NAT، أو اكتشاف الـ external-IP، أو خوارزميات تحديد الشبكات الفرعية — وهي كلها إما مستحيلة أو بلا معنى في شبكة I2P
ربط الهويةمجموعة سريعة واحدة مسموح بها لكل عنوان (destination). المستخدم الذي يملك عناوين كثيرة يحصل على مجموعة واحدة لكل عنوان — نفس الخاصية الوقائية ضد التلاعب (anti-gaming) التي يوفرها قناع IP في الشبكة العامة
الخصوصيةلا يتم إرسال عنوان IP قط، ولا يُستدل عليه ضمن الحسابات
عرض النطاق الترددييحل “Have All / Have None” محل الحقل البيتي الكامل في الـ torrent الكبيرة؛ ويزيل “Reject” الطلبات المكررة

الاعتبارات المتعلقة بالتنفيذ

  • هوية الند: يتم الحصول على تجزئة وجهة الند من اتصال البث (الوجهة الخاصة بالجلسة)، وهي القيمة نفسها التي يستخدمها كلا الطرفين. بالنسبة للاتصالات الصادرة، استخدم الوجهة التي قمت بالاتصال بها؛ وبالنسبة للواردة، استخدم الوجهة التي جاء منها الاتصال.
  • التفاوض: أرسل reserved[7] |= 0x04 في عملية الإمساك (handshake)؛ ولا تُرسَل رسائل Fast Extension إلا إذا كان الند قد عيّن نفس البت في إمساكه أيضًا؛ وإذا أرسل ند رسائل Fast Extension دون تفاوض، فاقطع الاتصال.
  • Have All / Have None: أرسل واحدة فقط من بين bitfield أو Have All أو Have None مباشرة بعد الإمساك. استخدم Have All للبذور (seeds)، وHave None حتى القطعة الأولى.
  • الجانب المرسل لـ Allowed Fast: عرّف فقط القطع التي تملكها فعليًا؛ إذ يمكن للمستقبل طلبها أثناء الحظر (choked). حدّ المجموعة التي تم خدمتها (مثلاً: ارفض طلبات allowed-fast من ند يحتفظ بالفعل بأكثر من k قطعة، وفقًا لتوجيهات BEP 6).
  • الجانب المستقبل لـ Allowed Fast: احفظ المجموعة؛ اسمح بطلبات تلك القطع أثناء الحظر؛ ويمكنك اختيار التحقق من المجموعة بإعادة حسابها باستخدام تجزئة وجهتك الخاصة ومعرّف المعلومات (infohash)، وتجاهل القطع غير الموجودة في المجموعة المحسوبة.
  • Reject: يجب أن يتلقى كل طلب ردًا واحدًا بالضبط؛ عند الحظر، ارفض كل شيء ليس ضمن مجموعة allowed fast بدلًا من إسكات الند بصمت.
  • حجم المجموعة: استخدم k = 10 لتحقيق التوافق؛ يُسمح للندود باختيار قيمة k أقل تحت الأحمال، ولكن كلا الطرفين يجب أن يعلن فقط عن ما يستطيع خدمته.
  • الحد على القطعة: يجب أن تستخدم index = y % sz عدد القطع الكلي sz الخاص بالتورنت؛ تجاهل الفهارس >= sz (احترازيًا)، لأن سلسلة التجزئة ليست مقيدة بنطاق القطعة.
  • التوافق العكسي: العميلات التي لا تتفاوض على البت السريع لن ترى هذه الرسائل مطلقًا؛ ولا تتطلب أي تغييرات أخرى في البروتوكول.

التنفيذات المرجعية

الخوارزمية صغيرة ومكتفية بذاتها — بضعة عشرات من الأسطر في أي لغة برمجة. الحسابات الثلاثة أدناه تعطي المجموعة نفسها عند إدخال نفس المدخلات (hash[0:4] ++ infohash، سلسلة SHA1، y % sz، مع تحديد k = 10).

جافا

// I2P: peer.getPeerID().getDestHash() is the 32-byte destination hash.
// Big-endian word reads build each candidate piece index from the SHA1 chain.
import java.security.MessageDigest;
import java.util.HashSet;
import java.util.Set;

public static Set<Integer> generateAllowedFastSet(byte[] destHash, byte[] infohash, int pieces) {
    Set<Integer> rv = new HashSet<>(10);
    if (destHash == null || infohash == null || pieces <= 0) {
        return rv;
    }
    byte[] x = new byte[24];
    System.arraycopy(destHash, 0, x, 0, 4);          // 4 hash bytes, no IP, no zeroed 4th byte
    System.arraycopy(infohash, 0, x, 4, Math.min(20, infohash.length));
    MessageDigest md = MessageDigest.getInstance("SHA-1");
    while (rv.size() < 10) {
        x = md.digest(x);
        for (int i = 0; i < 5 && rv.size() < 10; i++) {
            long y = ((x[i * 4] & 0xFFL) << 24) | ((x[i * 4 + 1] & 0xFFL) << 16)
                   | ((x[i * 4 + 2] & 0xFFL) << 8) | (x[i * 4 + 3] & 0xFFL);
            rv.add((int) (y % pieces));
        }
    }
    return rv;
}

C++

// Peer identity input is the 32-byte destination hash available on the connection.
#include <cstdint>
#include <set>
#include <vector>

extern std::vector<uint8_t> sha1(const std::vector<uint8_t>& in); // e.g. OpenSSL SHA1()

std::set<int> generate_allowed_fast_set(const std::vector<uint8_t>& dest_hash,
                                        const std::vector<uint8_t>& infohash,
                                        int pieces) {
    std::set<int> rv;
    if (dest_hash.size() < 4 || infohash.size() < 20 || pieces <= 0) { return rv; }
    std::vector<uint8_t> x(dest_hash.begin(), dest_hash.begin() + 4); // 4 hash bytes,
                                                                      // no IP mask
    x.insert(x.end(), infohash.begin(), infohash.begin() + 20);
    while (rv.size() < 10) {
        x = sha1(x);
        for (int i = 0; i < 5 && rv.size() < 10; i++) {
            uint32_t y = (uint32_t(x[i * 4]) << 24) | (uint32_t(x[i * 4 + 1]) << 16) |
                         (uint32_t(x[i * 4 + 2]) << 8) | uint32_t(x[i * 4 + 3]);
            rv.insert(int(y % uint32_t(pieces)));
        }
    }
    return rv;
}

بايثون

import hashlib

def generate_allowed_fast_set(dest_hash: bytes, infohash: bytes, pieces: int) -> set:
    """4 bytes of the destination hash stand in for the masked IP; no byte is zeroed."""
    rv = set()
    if len(dest_hash) < 4 or len(infohash) < 20 or pieces <= 0:
        return rv
    x = dest_hash[:4] + infohash[:20]
    while len(rv) < 10:
        x = hashlib.sha1(x).digest()
        for i in range(5):
            if len(rv) >= 10:
                break
            y = int.from_bytes(x[i * 4 : i * 4 + 4], "big")
            rv.add(y % pieces)
    return rv

التوافق

  • متوافق على مستوى السلك: بت التفاوض وتنسيقات الرسائل متطابقة بايتاً مع BEP 6 على الشبكة الواضحة؛ فقط يختلف مدخل توليد المجموعة.
  • غير قابل للتشغيل المتبادل عبر الشبكات: لا يمكن لعميل I2P وعميل شبكة واضحة الاتصال ببعضهما على أي حال؛ هذا الاختلاف يؤثر فقط على بايتات هوية الند، وليس أبداً على تنسيق السلك.
  • ضمن I2P: أي عميل يُنفذ هذا الاقتراح يحسب مجموعات “السريع المسموح” المسموح بها بشكل مطابق ويمكنه تقديمها والتحقق منها بالتبادل. أما العملاء الذين تتجاهل “السريع المسموح” فتعتبره ببساطة إشعارًا استشاريًا بلا تأثير، وتفقد فقط فائدة بدء التشغيل.

أسئلة مفتوحة

  1. هل يجب أن يبقى حجم المجموعة k ثابتًا عند 10، أم أن يكون متكيفًا مع الحمل (مثلاً أقل عند ارتفاع عدد الطلبات) كما يسمح به BEP 6؟
  2. هل يجب على المستقبل التحقق من المجموعة باستخدام تجزئة الوجهة الخاصة به ورفض الفهارس غير المتطابقة (لحماية ضد مرسلات خاطئة أو خبيثة)؟ يُوصى بالإجابة بنعم.
  3. اختيار البادئة ذات 4 بايت الأولى (البايتات 0-3) كما هو موضح، أو اختيار آخر 4 بايت — أي نافذة ثابتة بحجم 4 بايت تعطي الخصائص نفسها؛ استخدام البادئة يحافظ على ترتيب البايتات طبيعيًا كما في الكود المرجعي (hash[0:4]).

الفن السابق

  • المرجع: امتداد BitTorrent السريع (BEP 6)
  • التنفيذ المرجعي في I2PSnark: PeerState.sendAllowedFast() /
    generateAllowedFastSet() في
    apps/i2psnark/java/src/org/klomp/snark/PeerState.java (@since 0.9.71+)
  • يعمل بالتزامن مع BEP 40 (أولوية الندود القياسية) وBEP 21 (البذور الجزئية)، وكلاهما مدعوم في I2PSnark