انتقل إلى المحتوى الرئيسي
بلوكتشينMar 28, 2026

Deep EVM #11: جداول القفز في Huff — إرسال الدوال O(1) بدون حمل Solidity

OS
Open Soft Team

Engineering Team

مشكلة مرسل Solidity

عندما تستدعي عقد Solidity، أول ما تنفذه EVM هو مرسل الدوال. Solidity يولد سلسلة if-else خطية تقارن أول 4 بايت من calldata (محدد الدالة) مع كل محدد معروف:

CALLDATALOAD 0x00
SHR 224
DUP1
PUSH4 0x70a08231    // balanceOf(address)
EQ
PUSH2 dest1
JUMPI
...

لعقد بـ N دالة، هذا O(N) — أسوأ حالة تفحص جميع المحددات قبل العثور على تطابق. كل مقارنة تكلف ~22 غاز. عقد بـ 20 دالة يهدر حتى 440 غاز على الإرسال فقط.

لروبوت MEV يُستدعى ملايين المرات، تلك 400+ وحدة غاز لكل استدعاء تتراكم إلى ETH حقيقي.

نهج جدول القفز: O(1)

جدول القفز يربط محدد الدالة مباشرة بإزاحة الكود باستخدام الحساب، وليس المقارنة. الفكرة مستعارة من بنية المعالج — GOTOs المحسوبة استُخدمت منذ الستينيات.

المفهوم:

  1. استخراج محدد الدالة من calldata (4 بايت)
  2. استخدام الحساب لحساب وجهة القفز من المحدد
  3. القفز مباشرة إلى تلك الوجهة

لا مقارنات، لا تفريع، وقت ثابت بغض النظر عن عدد الدوال.

النهج 1: ترميز محدد بسيط

إذا كان عقدك يحتوي عدداً صغيراً من الدوال (1-8)، يمكنك تعيين المحددات يدوياً بتعدين محددات مخصصة حيث البايت الأول أو الثاني يرمز عدداً صحيحاً صغيراً فريداً:

#define macro DISPATCHER() = takes(0) returns(0) {
    0x00 calldataload       // [calldata_word]
    0xe0 shr                // [selector]
    0x18 shr                // [first_byte]
    0x02 mul                // [offset_in_table]
    __tablestart(JumpTable) // [table_start, offset_in_table]
    add                     // [entry_address]
    codecopy_dest:
    0x00 codecopy
    0x00 mload
    0xf0 shr
    jump
}

#define jumptable JumpTable {
    swap_exact
    add_liq
    remove_liq
    flash_loan
}

النهج 2: جدول كود محزوم

للكثافة القصوى، يمكنك حزم جدول القفز مباشرة في البايتكود باستخدام __tablestart و__tablesize. Huff يدعم جداول القفز كبنى أصلية.

مقارنة الغاز

الدوالSolidity (if-else)Solidity (ثنائي)جدول قفز Huff
222-44 غاز22-44 غاز15 غاز
422-88 غاز22-66 غاز15 غاز
822-176 غاز22-88 غاز15 غاز
1622-352 غاز22-110 غاز15 غاز
3222-704 غاز22-132 غاز15 غاز

تكلفة جدول القفز ثابتة: CALLDATALOAD (3) + SHR (3) + حساب (3-6) + JUMP (8) = ~15-18 غاز. لا تتغير أبداً بغض النظر عن عدد الدوال.

تعدين محددات مخصصة

لكي يعمل نهج جدول القفز، تحتاج محددات دوال ببايتات توجيه متوقعة:

import hashlib
import itertools

target_byte = 0x00
base_name = "swap"

for suffix in itertools.count():
    name = f"{base_name}{suffix}(uint256,address)"
    selector = hashlib.sha3_256(name.encode()).digest()[:4]
    if selector[0] == target_byte:
        print(f"Found: {name} -> 0x{selector.hex()}")
        break

تأثير حجم البايتكود

حجم البايتكود يؤثر مباشرة على تكلفة النشر (200 غاز لكل بايت عبر CREATE):

النهجبايتكود التشغيلغاز النشر
Solidity (8 دوال)~800 بايت160,000 غاز
جدول قفز Huff (8 دوال)~200 بايت40,000 غاز
Huff بسيط (دالتان)~61 بايت12,200 غاز

القيود

  1. ABI غير قياسي — الأدوات الخارجية لا تستطيع فك تشفير calldata بدون تعريفات ABI مخصصة
  2. تعدين المحددات — يتطلب عملاً مسبقاً ويقيد تسمية الدوال
  3. تكلفة الصيانة — Huff أصعب في التدقيق والتعديل من Solidity

الملخص

جداول القفز تستبدل سلسلة إرسال O(N) في Solidity بقفزات محسوبة O(1). التوفير في الغاز يتراكم عبر ملايين الاستدعاءات — ميزة ذات معنى للعقود عالية التردد مثل روبوتات MEV وموجهات DEX.