تخطي إلى المحتوى الرئيسي
Change page

إيثاش

كانت إيثاش خوارزمية تعدين إثبات العمل (PoW) الخاصة بشبكة إيثيريوم. تم الآن إيقاف تشغيل إثبات العمل بالكامل، وأصبحت إيثيريوم الآن مؤمنة باستخدام إثبات الحصة (PoS) بدلاً من ذلك. اقرأ المزيد عن الدمج، وإثبات الحصة، والتخزين. هذه الصفحة للأغراض التاريخية فقط!

إيثاش هي نسخة معدلة من خوارزمية Dagger-Hashimoto. إثبات العمل في إيثاش يعتمد بشكل كبير على الذاكرة (opens in a new tab)، وهو ما كان يُعتقد أنه يجعل الخوارزمية مقاومة لأجهزة ASIC. تم تطوير أجهزة ASIC الخاصة بإيثاش في النهاية، ولكن تعدين وحدة معالجة الرسومات (GPU) ظل خيارًا قابلاً للتطبيق حتى تم إيقاف تشغيل إثبات العمل. لا تزال إيثاش تُستخدم لتعدين عملات أخرى على شبكات إثبات العمل الأخرى غير إيثيريوم.

كيف تعمل إيثاش؟

يتم تحقيق الاعتماد الكبير على الذاكرة باستخدام خوارزمية إثبات العمل التي تتطلب اختيار مجموعات فرعية من مورد ثابت يعتمد على رقم فريد ورأس الكتلة. يُسمى هذا المورد (الذي يبلغ حجمه بضعة جيجابايت) DAG. يتم تغيير DAG كل 30,000 كتلة، وهي نافذة زمنية تبلغ حوالي 125 ساعة تُسمى حقبة (حوالي 5.2 أيام) وتستغرق بعض الوقت لإنشائها. نظرًا لأن DAG يعتمد فقط على ارتفاع الكتلة، يمكن إنشاؤه مسبقًا، ولكن إذا لم يتم ذلك، يحتاج العميل إلى الانتظار حتى نهاية هذه العملية لإنتاج كتلة. إذا لم يقم العملاء بإنشاء وتخزين DAGs مؤقتًا مسبقًا، فقد تواجه الشبكة تأخيرًا هائلاً في الكتل عند كل انتقال للحقبة. لاحظ أنه لا يلزم إنشاء DAG للتحقق من إثبات العمل، مما يسمح بشكل أساسي بالتحقق باستخدام وحدة معالجة مركزية (CPU) منخفضة وذاكرة صغيرة.

المسار العام الذي تتخذه الخوارزمية هو كما يلي:

  1. توجد بذرة (seed) يمكن حسابها لكل كتلة عن طريق مسح رؤوس الكتل حتى تلك النقطة.
  2. من البذرة، يمكن للمرء حساب ذاكرة تخزين مؤقت شبه عشوائية بحجم 16 MB. يقوم العملاء الخفيفون بتخزين الذاكرة المؤقتة.
  3. من الذاكرة المؤقتة، يمكننا إنشاء مجموعة بيانات بحجم 1 GB، مع خاصية أن كل عنصر في مجموعة البيانات يعتمد فقط على عدد صغير من العناصر من الذاكرة المؤقتة. يقوم العملاء الكاملون والمُعَدِّنون بتخزين مجموعة البيانات. تنمو مجموعة البيانات خطيًا مع مرور الوقت.
  4. يتضمن التعدين أخذ شرائح عشوائية من مجموعة البيانات وإجراء عملية التجزئة لها معًا. يمكن إجراء التحقق بذاكرة منخفضة باستخدام الذاكرة المؤقتة لإعادة إنشاء الأجزاء المحددة من مجموعة البيانات التي تحتاجها، لذلك تحتاج فقط إلى تخزين الذاكرة المؤقتة.

يتم تحديث مجموعة البيانات الكبيرة مرة واحدة كل 30,000 كتلة، لذلك فإن الغالبية العظمى من جهد المُعَدِّن ستكون في قراءة مجموعة البيانات، وليس إجراء تغييرات عليها.

التعريفات

نستخدم التعريفات التالية:

استخدام 'SHA3'

تزامن تطوير إيثيريوم مع تطوير معيار SHA3، وأجرت عملية المعايير تغييرًا متأخرًا في حشو خوارزمية التجزئة النهائية، بحيث لا تكون تجزئات "sha3_256" و"sha3_512" الخاصة بإيثيريوم تجزئات sha3 قياسية، بل هي متغير يُشار إليه غالبًا باسم "كيكاك-256" و"Keccak-512" في سياقات أخرى. انظر المناقشة، على سبيل المثال، هنا (opens in a new tab)، أو هنا (opens in a new tab)، أو هنا (opens in a new tab).

يرجى وضع ذلك في الاعتبار حيث يُشار إلى تجزئات "sha3" في وصف الخوارزمية أدناه.

المعلمات

تعتمد معلمات الذاكرة المؤقتة ومجموعة البيانات الخاصة بإيثاش على رقم الكتلة. ينمو كل من حجم الذاكرة المؤقتة وحجم مجموعة البيانات خطيًا؛ ومع ذلك، نأخذ دائمًا أعلى عدد أولي أقل من العتبة المتنامية خطيًا من أجل تقليل خطر الانتظامات العرضية التي تؤدي إلى سلوك دوري.

يتم توفير جداول قيم حجم مجموعة البيانات والذاكرة المؤقتة في الملحق.

إنشاء الذاكرة المؤقتة

الآن، نحدد الدالة لإنتاج ذاكرة مؤقتة:

تتضمن عملية إنتاج الذاكرة المؤقتة أولاً ملء 32 MB من الذاكرة بالتسلسل، ثم إجراء تمريرتين لخوارزمية RandMemoHash الخاصة بـ Sergio Demian Lerner من Strict Memory Hard Hashing Functions (2014) (opens in a new tab). المخرجات عبارة عن مجموعة من 524,288 قيمة بحجم 64-byte.

دالة تجميع البيانات

نستخدم خوارزمية مستوحاة من تجزئة FNV (opens in a new tab) في بعض الحالات كبديل غير ترابطي لـ XOR. لاحظ أننا نضرب العدد الأولي في المدخلات الكاملة بحجم 32-bit، على عكس مواصفات FNV-1 التي تضرب العدد الأولي في بايت واحد (ثماني) بدوره.

FNV_PRIME = 0x01000193

def fnv(v1, v2):
    return ((v1 * FNV_PRIME) ^ v2) % 2**32

يرجى ملاحظة أنه حتى الورقة الصفراء تحدد fnv كـ v1*(FNV_PRIME ^ v2)، فإن جميع التطبيقات الحالية تستخدم التعريف أعلاه باستمرار.

حساب مجموعة البيانات الكاملة

يتم حساب كل عنصر بحجم 64-byte في مجموعة البيانات الكاملة بحجم 1 GB على النحو التالي:

بشكل أساسي، نجمع البيانات من 256 عقدة ذاكرة مؤقتة محددة بشكل شبه عشوائي، ونقوم بإجراء عملية التجزئة لها لحساب عقدة مجموعة البيانات. يتم بعد ذلك إنشاء مجموعة البيانات بأكملها بواسطة:

def calc_dataset(full_size, cache):
    return [calc_dataset_item(cache, i) for i in range(full_size // HASH_BYTES)]

الحلقة الرئيسية

الآن، نحدد الحلقة الرئيسية الشبيهة بـ "hashimoto"، حيث نقوم بتجميع البيانات من مجموعة البيانات الكاملة من أجل إنتاج قيمتنا النهائية لرأس معين ورقم فريد. في الكود أدناه، يمثل header تجزئة SHA3-256 لتمثيل RLP لرأس كتلة مقتطع، أي لرأس يستبعد الحقلين mixHash وnonce. nonce هي الثمانية بايتات لعدد صحيح غير موقّع بحجم 64 bit في نظام النهاية الكبرى. لذا فإن nonce[::-1] هو تمثيل النهاية الصغرى المكون من ثمانية بايتات لتلك القيمة:

بشكل أساسي، نحافظ على "مزيج" بعرض 128 bytes، ونجلب بشكل متكرر ومتسلسل 128 bytes من مجموعة البيانات الكاملة ونستخدم الدالة fnv لدمجها مع المزيج. يتم استخدام 128 bytes من الوصول المتسلسل بحيث تجلب كل جولة من الخوارزمية دائمًا صفحة كاملة من ذاكرة الوصول العشوائي (RAM)، مما يقلل من أخطاء المخزن المؤقت للترجمة (TLB) التي يمكن لأجهزة ASIC تجنبها نظريًا.

إذا كان ناتج هذه الخوارزمية أقل من الهدف المطلوب، فإن الرقم الفريد صالح. لاحظ أن التطبيق الإضافي لـ sha3_256 في النهاية يضمن وجود رقم فريد وسيط يمكن تقديمه لإثبات أنه تم إنجاز قدر صغير على الأقل من العمل؛ يمكن استخدام هذا التحقق السريع الخارجي من إثبات العمل (PoW) لأغراض مكافحة هجمات حجب الخدمة الموزعة (DDoS). كما أنه يعمل على توفير ضمان إحصائي بأن النتيجة هي رقم غير متحيز بحجم 256-bit.

التعدين

يتم تعريف خوارزمية التعدين على النحو التالي:

def mine(full_size, dataset, header, difficulty):
    # حشو الهدف بالأصفار لمقارنته مع التجزئة على نفس الخانة
    target = zpad(encode_int(2**256 // difficulty), 64)[::-1]
    from random import randint
    nonce = randint(0, 2**64)
    while hashimoto_full(full_size, dataset, header, nonce) > target:
        nonce = (nonce + 1) % 2**64
    return nonce

تحديد تجزئة البذرة

من أجل حساب تجزئة البذرة التي سيتم استخدامها للتعدين فوق كتلة معينة، نستخدم الخوارزمية التالية:

 def get_seedhash(block):
     s = '\x00' * 32
     for i in range(block.number // EPOCH_LENGTH):
         s = serialize_hash(sha3_256(s))
     return s

لاحظ أنه من أجل التعدين والتحقق السلس، نوصي بالحساب المسبق لتجزئات البذور ومجموعات البيانات المستقبلية في مسار (thread) منفصل.

قراءة إضافية

هل تعرف موردًا مجتمعيًا ساعدك؟ قم بتعديل هذه الصفحة وأضفه!

الملحق

يجب إلحاق الكود التالي في البداية إذا كنت مهتمًا بتشغيل مواصفات Python أعلاه ككود.

أحجام البيانات

توفر جداول البحث التالية ما يقرب من 2048 حقبة مجدولة لأحجام البيانات وأحجام الذاكرة المؤقتة.