---
title: "فهم مواصفات ⁦EVM⁩ في الورقة الصفراء"
description: "فهم الجزء الخاص بالورقة الصفراء، وهي المواصفات الرسمية لإيثيريوم، والذي يشرح آلة إيثيريوم الافتراضية (⁦EVM⁩)."
author: "qbzzt"
tags: ["evm"]
skill: intermediate
breadcrumb: "الورقة الصفراء لـ ⁦EVM⁩"
lang: ar
published: 2022-05-15
---

[الورقة الصفراء](https://ethereum.github.io/yellowpaper/paper.pdf) هي المواصفات الرسمية لإيثيريوم. باستثناء ما تم تعديله بواسطة [عملية مقترحات تحسين إيثيريوم (EIP)](/eips/)، فإنها تحتوي على الوصف الدقيق لكيفية عمل كل شيء. تمت كتابتها كورقة بحثية رياضية، والتي تتضمن مصطلحات قد لا يجدها المبرمجون مألوفة. في هذه المقالة، ستتعلم كيفية قراءتها، وبالتالي قراءة الأوراق الرياضية الأخرى ذات الصلة.

## أي ورقة صفراء؟ {#which-yellow-paper}

مثل كل شيء تقريبًا في إيثيريوم، تتطور الورقة الصفراء بمرور الوقت. لكي أتمكن من الإشارة إلى إصدار معين، قمت برفع [الإصدار الحالي وقت الكتابة](https://ethereum.github.io/yellowpaper/paper.pdf). ستشير أرقام الأقسام والصفحات والمعادلات التي أستخدمها إلى ذلك الإصدار. من الجيد إبقاؤها مفتوحة في نافذة مختلفة أثناء قراءة هذا المستند.

### لماذا <span dir="ltr">EVM</span>؟ {#why-the-evm}

كُتبت الورقة الصفراء الأصلية في بداية تطوير إيثيريوم. وهي تصف آلية الإجماع الأصلية القائمة على إثبات العمل (PoW) التي استُخدمت في الأصل لتأمين الشبكة. ومع ذلك، أوقفت إيثيريوم إثبات العمل (PoW) وبدأت في استخدام الإجماع القائم على إثبات الحصة (PoS) في سبتمبر 2022. سيركز هذا البرنامج التعليمي على أجزاء الورقة الصفراء التي تُعرّف آلة إيثيريوم الافتراضية (EVM). لم تتغير <span dir="ltr">EVM</span> بالانتقال إلى إثبات الحصة (PoS) (باستثناء القيمة المرجعة لرمز التشغيل DIFFICULTY).

## 9 نموذج التنفيذ

يتضمن هذا القسم (ص <span dir="ltr">14-16</span>) معظم تعريف <span dir="ltr">EVM</span>.

يتضمن مصطلح _حالة النظام (system state)_ كل ما تحتاج لمعرفته حول النظام لتشغيله. في الحاسوب النموذجي، يعني هذا الذاكرة، ومحتوى السجلات، وما إلى ذلك.

[آلة تورنغ (Turing machine)](https://en.wikipedia.org/wiki/Turing_machine) هي نموذج حسابي. في الأساس، هي نسخة مبسطة من الحاسوب، والتي ثبت أن لديها نفس القدرة على تشغيل العمليات الحسابية التي يمكن للحاسوب العادي القيام بها (كل ما يمكن للحاسوب حسابه يمكن لآلة تورنغ حسابه والعكس صحيح). يسهل هذا النموذج إثبات النظريات المختلفة حول ما هو قابل للحساب وما هو غير قابل للحساب.

يعني مصطلح [مكتملة تورنغ (Turing-complete)](https://en.wikipedia.org/wiki/Turing_completeness) حاسوبًا يمكنه تشغيل نفس الحسابات مثل آلة تورنغ. يمكن لآلات تورنغ الدخول في حلقات لا نهائية، ولا يمكن لـ <span dir="ltr">EVM</span> القيام بذلك لأنها ستنفد من الغاز، لذا فهي شبه مكتملة تورنغ فقط.
## 9.1 الأساسيات {#91-basics}

يقدم هذا القسم أساسيات <span dir="ltr">EVM</span> وكيفية مقارنتها بالنماذج الحسابية الأخرى.

[آلة المكدس (stack machine)](https://en.wikipedia.org/wiki/Stack_machine) هي حاسوب يخزن البيانات الوسيطة ليس في السجلات، ولكن في [**مكدس (stack)**](<https://en.wikipedia.org/wiki/Stack_(abstract_data_type)>). هذه هي البنية المفضلة للآلات الافتراضية لأنه من السهل تنفيذها مما يعني أن الأخطاء والثغرات الأمنية أقل احتمالًا بكثير. تنقسم الذاكرة في المكدس إلى كلمات بحجم <span dir="ltr">256-bit</span>. تم اختيار هذا لأنه مناسب لعمليات التشفير الأساسية في إيثيريوم مثل عملية التجزئة كيكاك-256 وحسابات منحنى إهليلجي. الحد الأقصى لحجم المكدس هو <span dir="ltr">1024</span> عنصرًا (<span dir="ltr">1024 x 256 bits</span>). عند تنفيذ رموز التشغيل، فإنها عادة ما تحصل على معاملاتها من المكدس. هناك رموز تشغيل مخصصة لإعادة تنظيم العناصر في المكدس مثل `POP` (يزيل العنصر من أعلى المكدس)، و `DUP_N` (يكرر العنصر رقم N في المكدس)، وما إلى ذلك.

تحتوي <span dir="ltr">EVM</span> أيضًا على مساحة متطايرة تسمى **الذاكرة (memory)** والتي تُستخدم لتخزين البيانات أثناء التنفيذ. يتم تنظيم هذه الذاكرة في كلمات بحجم <span dir="ltr">32-byte</span>. تتم تهيئة جميع مواقع الذاكرة إلى الصفر. إذا قمت بتنفيذ كود [Yul](https://docs.soliditylang.org/en/latest/yul.html) هذا لإضافة كلمة إلى الذاكرة، فسيملأ <span dir="ltr">32 bytes</span> من الذاكرة عن طريق حشو المساحة الفارغة في الكلمة بالأصفار، أي أنه ينشئ كلمة واحدة - مع أصفار في المواقع <span dir="ltr">0-29</span>، و <span dir="ltr">0x60</span> إلى <span dir="ltr">30</span>، و <span dir="ltr">0xA7</span> إلى <span dir="ltr">31</span>.

```yul
mstore(0, 0x60A7)
```

`mstore` هو أحد رموز التشغيل الثلاثة التي توفرها <span dir="ltr">EVM</span> للتفاعل مع الذاكرة - فهو يحمل كلمة في الذاكرة. الاثنان الآخران هما `mstore8` الذي يحمل بايت واحد في الذاكرة، و `mload` الذي ينقل كلمة من الذاكرة إلى المكدس.

تحتوي <span dir="ltr">EVM</span> أيضًا على نموذج **تخزين (storage)** منفصل غير متطاير يتم الحفاظ عليه كجزء من حالة النظام - يتم تنظيم هذه الذاكرة في مصفوفات كلمات (على عكس مصفوفات البايت القابلة للعنونة بالكلمات في المكدس). هذا التخزين هو المكان الذي تحتفظ فيه العقود بالبيانات الدائمة - لا يمكن للعقد التفاعل إلا مع التخزين الخاص به. يتم تنظيم التخزين في تعيينات مفتاح-قيمة.

على الرغم من عدم ذكره في هذا القسم من الورقة الصفراء، فمن المفيد أيضًا معرفة أن هناك نوعًا رابعًا من الذاكرة. **بيانات الاستدعاء (Calldata)** هي ذاكرة للقراءة فقط قابلة للعنونة بالبايت تُستخدم لتخزين القيمة الممررة مع معامل `data` لمعاملة. تحتوي <span dir="ltr">EVM</span> على رموز تشغيل محددة لإدارة `calldata`. `calldatasize` يُرجع حجم البيانات. `calldataload` يُحمل البيانات في المكدس. `calldatacopy` ينسخ البيانات إلى الذاكرة.

تقوم [بنية فون نيومان (Von Neumann architecture)](https://en.wikipedia.org/wiki/Von_Neumann_architecture) القياسية بتخزين الكود والبيانات في نفس الذاكرة. لا تتبع <span dir="ltr">EVM</span> هذا المعيار لأسباب أمنية - فمشاركة الذاكرة المتطايرة تجعل من الممكن تغيير كود البرنامج. بدلاً من ذلك، يتم حفظ الكود في التخزين.

هناك حالتان فقط يتم فيهما تنفيذ الكود من الذاكرة:

- عندما يقوم عقد بإنشاء عقد آخر (باستخدام [`CREATE`](https://www.evm.codes/#f0) أو [`CREATE2`](https://www.evm.codes/#f5))، فإن كود مُنشئ العقد يأتي من الذاكرة.
- أثناء إنشاء _أي_ عقد، يتم تشغيل كود المُنشئ ثم يعود بكود العقد الفعلي، من الذاكرة أيضًا.

يعني مصطلح التنفيذ الاستثنائي استثناءً يتسبب في توقف تنفيذ العقد الحالي.

## 9.2 نظرة عامة على الرسوم {#92-fees-overview}

يشرح هذا القسم كيفية حساب رسوم الغاز. هناك ثلاث تكاليف:

### تكلفة رمز التشغيل

التكلفة المتأصلة لرمز التشغيل المحدد. للحصول على هذه القيمة، ابحث عن مجموعة تكلفة رمز التشغيل في الملحق H (ص <span dir="ltr">29</span>، تحت المعادلة (<span dir="ltr">329</span>))، وابحث عن مجموعة التكلفة في المعادلة (<span dir="ltr">326</span>). يمنحك هذا دالة تكلفة، والتي تستخدم في معظم الحالات معلمات من الملحق G (ص <span dir="ltr">28</span>).

على سبيل المثال، رمز التشغيل [`CALLDATACOPY`](https://www.evm.codes/#37) هو عضو في المجموعة <span dir="ltr">_W<sub>copy</sub>_</span>. تكلفة رمز التشغيل لتلك المجموعة هي <span dir="ltr">_G<sub>verylow</sub>+G<sub>copy</sub>×⌈μ<sub>s</sub>[2]÷32⌉_</span>. بالنظر إلى الملحق G، نرى أن كلا الثابتين هما <span dir="ltr">3</span>، مما يعطينا <span dir="ltr">_3+3×⌈μ<sub>s</sub>[2]÷32⌉_</span>.

لا يزال يتعين علينا فك تشفير التعبير <span dir="ltr">_⌈μ<sub>s</sub>[2]÷32⌉_</span>. الجزء الخارجي، <span dir="ltr">_⌈ \<value\> ⌉_</span> هو دالة السقف (ceiling function)، وهي دالة تُرجع عند إعطائها قيمة أصغر عدد صحيح لا يزال غير أصغر من القيمة. على سبيل المثال، <span dir="ltr">_⌈2.5⌉ = ⌈3⌉ = 3_</span>. الجزء الداخلي هو <span dir="ltr">_μ<sub>s</sub>[2]÷32_</span>. بالنظر إلى القسم <span dir="ltr">3</span> (الاصطلاحات) في ص <span dir="ltr">3</span>، <span dir="ltr">_μ_</span> هي حالة الآلة. يتم تعريف حالة الآلة في القسم <span dir="ltr">9.4.1</span> في ص <span dir="ltr">15</span>. وفقًا لذلك القسم، فإن إحدى معلمات حالة الآلة هي <span dir="ltr">_s_</span> للمكدس. بجمع كل ذلك معًا، يبدو أن <span dir="ltr">_μ<sub>s</sub>[2]_</span> هو الموقع رقم <span dir="ltr">2</span> في المكدس. بالنظر إلى [رمز التشغيل](https://www.evm.codes/#37)، فإن الموقع رقم <span dir="ltr">2</span> في المكدس هو حجم البيانات بالبايت. بالنظر إلى رموز التشغيل الأخرى في المجموعة <span dir="ltr">W<sub>copy</sub></span>، وهي [`CODECOPY`](https://www.evm.codes/#39) و [`RETURNDATACOPY`](https://www.evm.codes/#3e)، فإن لديها أيضًا حجم بيانات في نفس الموقع. لذا فإن <span dir="ltr">_⌈μ<sub>s</sub>[2]÷32⌉_</span> هو عدد الكلمات بحجم <span dir="ltr">32-byte</span> المطلوبة لتخزين البيانات التي يتم نسخها. بجمع كل شيء معًا، فإن التكلفة المتأصلة لـ [`CALLDATACOPY`](https://www.evm.codes/#37) هي <span dir="ltr">3</span> غاز زائد <span dir="ltr">3</span> لكل كلمة من البيانات التي يتم نسخها.
### تكلفة التشغيل {#running-cost}

تكلفة تشغيل الكود الذي نستدعيه.

- في حالة [`CREATE`](https://www.evm.codes/#f0) و [`CREATE2`](https://www.evm.codes/#f5)، مُنشئ العقد الجديد.
- في حالة [`CALL`](https://www.evm.codes/#f1)، أو [`CALLCODE`](https://www.evm.codes/#f2)، أو [`STATICCALL`](https://www.evm.codes/#fa)، أو [`DELEGATECALL`](https://www.evm.codes/#f4)، العقد الذي نستدعيه.

### تكلفة توسيع الذاكرة

تكلفة توسيع الذاكرة (إذا لزم الأمر).

في المعادلة <span dir="ltr">326</span>، تُكتب هذه القيمة كـ <span dir="ltr">_C<sub>mem</sub>(μ<sub>i</sub>')-C<sub>mem</sub>(μ<sub>i</sub>)_</span>. بالنظر إلى القسم <span dir="ltr">9.4.1</span> مرة أخرى، نرى أن <span dir="ltr">_μ<sub>i</sub>_</span> هو عدد الكلمات في الذاكرة. لذا فإن <span dir="ltr">_μ<sub>i</sub>_</span> هو عدد الكلمات في الذاكرة قبل رمز التشغيل و <span dir="ltr">_μ<sub>i</sub>'_</span> هو عدد الكلمات في الذاكرة بعد رمز التشغيل.

يتم تعريف الدالة <span dir="ltr">_C<sub>mem</sub>_</span> في المعادلة <span dir="ltr">328</span>: <span dir="ltr">_C<sub>mem</sub>(a) = G<sub>memory</sub> × a + ⌊a<sup>2</sup> ÷ 512⌋_</span>. <span dir="ltr">_⌊x⌋_</span> هي دالة الأرضية (floor function)، وهي دالة تُرجع عند إعطائها قيمة أكبر عدد صحيح لا يزال غير أكبر من القيمة. على سبيل المثال، <span dir="ltr">_⌊2.5⌋ = ⌊2⌋ = 2._</span> عندما يكون <span dir="ltr">_a < √512_</span>، فإن <span dir="ltr">_a<sup>2</sup> < 512_</span>، وتكون نتيجة دالة الأرضية صفرًا. لذا بالنسبة لأول <span dir="ltr">22</span> كلمة (<span dir="ltr">704 bytes</span>)، ترتفع التكلفة خطيًا مع عدد كلمات الذاكرة المطلوبة. بعد تلك النقطة يكون <span dir="ltr">_⌊a<sup>2</sup> ÷ 512⌋_</span> موجبًا. عندما تكون الذاكرة المطلوبة عالية بما يكفي، تكون تكلفة الغاز متناسبة مع مربع مقدار الذاكرة.

**ملاحظة** أن هذه العوامل تؤثر فقط على تكلفة الغاز _المتأصلة_ - فهي لا تأخذ في الاعتبار سوق الرسوم أو الإكراميات للمدققين التي تحدد المبلغ الذي يُطلب من المستخدم النهائي دفعه - هذه مجرد التكلفة الأولية لتشغيل عملية معينة على <span dir="ltr">EVM</span>.

[اقرأ المزيد عن الغاز](/developers/docs/gas/).
## 9.3 بيئة التنفيذ

بيئة التنفيذ هي صف (tuple)، <span dir="ltr">_I_</span>، يتضمن معلومات ليست جزءًا من حالة سلسلة الكتل أو <span dir="ltr">EVM</span>.

| المعلمة | رمز التشغيل للوصول إلى البيانات | كود Solidity للوصول إلى البيانات |
| --------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| <span dir="ltr">_I<sub>a</sub>_</span> | [`ADDRESS`](https://www.evm.codes/#30) | `address(this)` |
| <span dir="ltr">_I<sub>o</sub>_</span> | [`ORIGIN`](https://www.evm.codes/#32) | `tx.origin` |
| <span dir="ltr">_I<sub>p</sub>_</span> | [`GASPRICE`](https://www.evm.codes/#3a) | `tx.gasprice` |
| <span dir="ltr">_I<sub>d</sub>_</span> | [`CALLDATALOAD`](https://www.evm.codes/#35)، إلخ. | `msg.data` |
| <span dir="ltr">_I<sub>s</sub>_</span> | [`CALLER`](https://www.evm.codes/#33) | `msg.sender` |
| <span dir="ltr">_I<sub>v</sub>_</span> | [`CALLVALUE`](https://www.evm.codes/#34) | `msg.value` |
| <span dir="ltr">_I<sub>b</sub>_</span> | [`CODECOPY`](https://www.evm.codes/#39) | `address(this).code` |
| <span dir="ltr">_I<sub>H</sub>_</span> | حقول رأس الكتلة، مثل [`NUMBER`](https://www.evm.codes/#43) و [`DIFFICULTY`](https://www.evm.codes/#44) | `block.number`، `block.difficulty`، إلخ. |
| <span dir="ltr">_I<sub>e</sub>_</span> | عمق مكدس الاستدعاء للاستدعاءات بين العقود (بما في ذلك إنشاء العقد) |
| <span dir="ltr">_I<sub>w</sub>_</span> | هل يُسمح لـ <span dir="ltr">EVM</span> بتغيير الحالة، أم أنها تعمل بشكل ثابت |

هناك بعض المعلمات الأخرى الضرورية لفهم بقية القسم <span dir="ltr">9</span>:

| المعلمة | محددة في القسم | المعنى |
| --------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <span dir="ltr">_σ_</span> | <span dir="ltr">2</span> (ص <span dir="ltr">2</span>، المعادلة <span dir="ltr">1</span>) | حالة سلسلة الكتل |
| <span dir="ltr">_g_</span> | <span dir="ltr">9.3</span> (ص <span dir="ltr">14</span>) | الغاز المتبقي |
| <span dir="ltr">_A_</span> | <span dir="ltr">6.1</span> (ص <span dir="ltr">9</span>) | الحالة الفرعية المتراكمة (التغييرات المجدولة عند انتهاء المعاملة) |
| <span dir="ltr">_o_</span> | <span dir="ltr">9.3</span> (ص <span dir="ltr">14</span>) | المخرجات - النتيجة المرجعة في حالة معاملة داخلية (عندما يستدعي عقد عقدًا آخر) واستدعاءات دوال العرض (عندما تطلب معلومات فقط، لذلك لا داعي لانتظار معاملة) |
## 9.4 نظرة عامة على التنفيذ

الآن بعد أن أصبح لدينا جميع المقدمات، يمكننا أخيرًا البدء في العمل على كيفية عمل <span dir="ltr">EVM</span>.

تعطينا المعادلات <span dir="ltr">146-151</span> الشروط الأولية لتشغيل <span dir="ltr">EVM</span>:

| الرمز | القيمة الأولية | المعنى |
| ---------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <span dir="ltr">_μ<sub>g</sub>_</span> | <span dir="ltr">_g_</span> | الغاز المتبقي |
| <span dir="ltr">_μ<sub>pc</sub>_</span> | <span dir="ltr">_0_</span> | عداد البرنامج، عنوان التعليمة التالية المراد تنفيذها |
| <span dir="ltr">_μ<sub>m</sub>_</span> | <span dir="ltr">_(0, 0, ...)_</span> | الذاكرة، مهيأة كلها بالأصفار |
| <span dir="ltr">_μ<sub>i</sub>_</span> | <span dir="ltr">_0_</span> | أعلى موقع ذاكرة مستخدم |
| <span dir="ltr">_μ<sub>s</sub>_</span> | <span dir="ltr">_()_</span> | المكدس، فارغ في البداية |
| <span dir="ltr">_μ<sub>o</sub>_</span> | <span dir="ltr">_∅_</span> | المخرجات، مجموعة فارغة حتى وما لم نتوقف إما ببيانات مرجعة ([`RETURN`](https://www.evm.codes/#f3) أو [`REVERT`](https://www.evm.codes/#fd)) أو بدونها ([`STOP`](https://www.evm.codes/#00) أو [`SELFDESTRUCT`](https://www.evm.codes/#ff)). |

تخبرنا المعادلة <span dir="ltr">152</span> أن هناك أربعة شروط محتملة في كل نقطة زمنية أثناء التنفيذ، وما يجب فعله معها:

1.  `Z(σ,μ,A,I)`. يمثل Z دالة تختبر ما إذا كانت العملية تنشئ انتقال حالة غير صالح (انظر [التوقف الاستثنائي](#942-exceptional-halt)). إذا تم تقييمها إلى صحيح (True)، فإن الحالة الجديدة مطابقة للحالة القديمة (باستثناء احتراق الغاز) لأن التغييرات لم يتم تنفيذها.
2.  إذا كان رمز التشغيل الذي يتم تنفيذه هو [`REVERT`](https://www.evm.codes/#fd)، فإن الحالة الجديدة هي نفس الحالة القديمة، ويُفقد بعض الغاز.
3.  إذا انتهى تسلسل العمليات، كما هو موضح بواسطة [`RETURN`](https://www.evm.codes/#f3)، يتم تحديث الحالة إلى الحالة الجديدة.
4.  إذا لم نكن في أحد شروط النهاية <span dir="ltr">1-3</span>، فاستمر في التشغيل.
## 9.4.1 حالة الآلة {#941-machine-state}

يشرح هذا القسم حالة الآلة بمزيد من التفصيل. يحدد أن <span dir="ltr">_w_</span> هو رمز التشغيل الحالي. إذا كان <span dir="ltr">_μ<sub>pc</sub>_</span> أقل من <span dir="ltr">_||I<sub>b</sub>||_</span>، وهو طول الكود، فإن ذلك البايت (<span dir="ltr">_I<sub>b</sub>[μ<sub>pc</sub>]_</span>) هو رمز التشغيل. بخلاف ذلك، يتم تعريف رمز التشغيل على أنه [`STOP`](https://www.evm.codes/#00).

نظرًا لأن هذه [آلة مكدس](https://en.wikipedia.org/wiki/Stack_machine)، نحتاج إلى تتبع عدد العناصر التي تم إخراجها (<span dir="ltr">_δ_</span>) وإدخالها (<span dir="ltr">_α_</span>) بواسطة كل رمز تشغيل.

## 9.4.2 التوقف الاستثنائي

يحدد هذا القسم الدالة <span dir="ltr">_Z_</span>، التي تحدد متى يكون لدينا إنهاء غير طبيعي. هذه دالة [منطقية (Boolean)](https://en.wikipedia.org/wiki/Boolean_data_type)، لذا فهي تستخدم [<span dir="ltr">_∨_</span> لـ "أو" المنطقية](https://en.wikipedia.org/wiki/Logical_disjunction) و [<span dir="ltr">_∧_</span> لـ "و" المنطقية](https://en.wikipedia.org/wiki/Logical_conjunction).

لدينا توقف استثنائي إذا كان أي من هذه الشروط صحيحًا:

- **<span dir="ltr">_μ<sub>g</sub> < C(σ,μ,A,I)_</span>**
  كما رأينا في القسم <span dir="ltr">9.2</span>، <span dir="ltr">_C_</span> هي الدالة التي تحدد تكلفة الغاز. لا يوجد غاز كافٍ متبقٍ لتغطية رمز التشغيل التالي.

- **<span dir="ltr">_δ<sub>w</sub>=∅_</span>**
  إذا كان عدد العناصر المخرجة لرمز تشغيل غير محدد، فإن رمز التشغيل نفسه غير محدد.

- **<span dir="ltr">_|| μ<sub>s</sub> || < δ<sub>w</sub>_</span>**
  نقص سعة المكدس (Stack underflow)، لا توجد عناصر كافية في المكدس لرمز التشغيل الحالي.

- **<span dir="ltr">_w = JUMP ∧ μ<sub>s</sub>[0]∉D(I<sub>b</sub>)_</span>**
  رمز التشغيل هو [`JUMP`](https://www.evm.codes/#56) والعنوان ليس [`JUMPDEST`](https://www.evm.codes/#5b). تكون القفزات صالحة _فقط_ عندما تكون الوجهة [`JUMPDEST`](https://www.evm.codes/#5b).

- **<span dir="ltr">_w = JUMPI ∧ μ<sub>s</sub>[1]≠0 ∧ μ<sub>s</sub>[0] ∉ D(I<sub>b</sub>)_</span>**
  رمز التشغيل هو [`JUMPI`](https://www.evm.codes/#57)، والشرط صحيح (غير صفري) لذا يجب أن تحدث القفزة، والعنوان ليس [`JUMPDEST`](https://www.evm.codes/#5b). تكون القفزات صالحة _فقط_ عندما تكون الوجهة [`JUMPDEST`](https://www.evm.codes/#5b).

- **<span dir="ltr">_w = RETURNDATACOPY ∧ μ<sub>s</sub>[1]+μ<sub>s</sub>[2]>|| μ<sub>o</sub> ||_</span>**
  رمز التشغيل هو [`RETURNDATACOPY`](https://www.evm.codes/#3e). في رمز التشغيل هذا، عنصر المكدس <span dir="ltr">_μ<sub>s</sub>[1]_</span> هو الإزاحة للقراءة منها في المخزن المؤقت للبيانات المرجعة، وعنصر المكدس <span dir="ltr">_μ<sub>s</sub>[2]_</span> هو طول البيانات. يحدث هذا الشرط عندما تحاول القراءة بعد نهاية المخزن المؤقت للبيانات المرجعة. لاحظ أنه لا يوجد شرط مماثل لبيانات الاستدعاء أو للكود نفسه. عندما تحاول القراءة بعد نهاية تلك المخازن المؤقتة، فإنك تحصل على أصفار فقط.

- **<span dir="ltr">_|| μ<sub>s</sub> || - δ<sub>w</sub> + α<sub>w</sub> > 1024_</span>**

  تجاوز سعة المكدس. إذا كان تشغيل رمز التشغيل سيؤدي إلى مكدس يحتوي على أكثر من <span dir="ltr">1024</span> عنصرًا، فقم بالإحباط.

- **<span dir="ltr">_¬I<sub>w</sub> ∧ W(w,μ)_</span>**
  هل نعمل بشكل ثابت ([<span dir="ltr">¬</span> هو النفي](https://en.wikipedia.org/wiki/Negation) و <span dir="ltr">_I<sub>w</sub>_</span> صحيح عندما يُسمح لنا بتغيير حالة سلسلة الكتل)؟ إذا كان الأمر كذلك، وكنا نحاول إجراء عملية تغيير حالة، فلا يمكن أن يحدث ذلك.

  يتم تعريف الدالة <span dir="ltr">_W(w,μ)_</span> لاحقًا في المعادلة <span dir="ltr">159</span>. تكون <span dir="ltr">_W(w,μ)_</span> صحيحة إذا كان أحد هذه الشروط صحيحًا:

  - **<span dir="ltr">_w ∈ \{CREATE, CREATE2, SSTORE, SELFDESTRUCT}_</span>**
    تغير رموز التشغيل هذه الحالة، إما عن طريق إنشاء عقد جديد، أو تخزين قيمة، أو تدمير ذاتي للعقد الحالي.

  - **<span dir="ltr">_LOG0≤w ∧ w≤LOG4_</span>**
    إذا تم استدعاؤنا بشكل ثابت، فلا يمكننا إصدار إدخالات سجل.
    تقع جميع رموز تشغيل السجل في النطاق بين [`LOG0` (A0)](https://www.evm.codes/#a0) و [`LOG4` (A4)](https://www.evm.codes/#a4).
    يحدد الرقم بعد رمز تشغيل السجل عدد المواضيع التي يحتوي عليها إدخال السجل.
  - **<span dir="ltr">_w=CALL ∧ μ<sub>s</sub>[2]≠0_</span>**
    يمكنك استدعاء عقد آخر عندما تكون ثابتًا، ولكن إذا قمت بذلك فلا يمكنك تحويل ETH إليه.

- **<span dir="ltr">_w = SSTORE ∧ μ<sub>g</sub> ≤ G<sub>callstipend</sub>_</span>**
  لا يمكنك تشغيل [`SSTORE`](https://www.evm.codes/#55) ما لم يكن لديك أكثر من <span dir="ltr">G<sub>callstipend</sub></span> (مُعرّف كـ <span dir="ltr">2300</span> في الملحق G) غاز.
## 9.4.3 صلاحية وجهة القفز

هنا نحدد رسميًا ما هي رموز التشغيل [`JUMPDEST`](https://www.evm.codes/#5b). لا يمكننا فقط البحث عن قيمة البايت <span dir="ltr">0x5B</span>، لأنها قد تكون داخل PUSH (وبالتالي فهي بيانات وليست رمز تشغيل).

في المعادلة (<span dir="ltr">162</span>) نحدد دالة، <span dir="ltr">_N(i,w)_</span>. المعلمة الأولى، <span dir="ltr">_i_</span>، هي موقع رمز التشغيل. والثانية، <span dir="ltr">_w_</span>، هي رمز التشغيل نفسه. إذا كان <span dir="ltr">_w∈[PUSH1, PUSH32]_</span> فهذا يعني أن رمز التشغيل هو PUSH (تحدد الأقواس المربعة نطاقًا يتضمن نقاط النهاية). في هذه الحالة، يكون رمز التشغيل التالي في <span dir="ltr">_i+2+(w−PUSH1)_</span>. بالنسبة لـ [`PUSH1`](https://www.evm.codes/#60) نحتاج إلى التقدم بمقدار بايتين (PUSH نفسه وقيمة البايت الواحد)، وبالنسبة لـ [`PUSH2`](https://www.evm.codes/#61) نحتاج إلى التقدم بمقدار ثلاثة بايتات لأنها قيمة من بايتين، وهكذا. جميع رموز تشغيل <span dir="ltr">EVM</span> الأخرى يبلغ طولها بايتًا واحدًا فقط، لذا في جميع الحالات الأخرى <span dir="ltr">_N(i,w)=i+1_</span>.

تُستخدم هذه الدالة في المعادلة (<span dir="ltr">161</span>) لتعريف <span dir="ltr">_D<sub>J</sub>(c,i)_</span>، وهي [مجموعة (set)](<https://en.wikipedia.org/wiki/Set_(mathematics)>) جميع وجهات القفز الصالحة في الكود <span dir="ltr">_c_</span>، بدءًا من موقع رمز التشغيل <span dir="ltr">_i_</span>. يتم تعريف هذه الدالة بشكل متكرر. إذا كان <span dir="ltr">_i≥||c||_</span>، فهذا يعني أننا في نهاية الكود أو بعدها. لن نجد أي وجهات قفز أخرى، لذا أرجع المجموعة الفارغة فقط.

في جميع الحالات الأخرى، ننظر إلى بقية الكود بالانتقال إلى رمز التشغيل التالي والحصول على المجموعة بدءًا منه. <span dir="ltr">_c[i]_</span> هو رمز التشغيل الحالي، لذا فإن <span dir="ltr">_N(i,c[i])_</span> هو موقع رمز التشغيل التالي. وبالتالي فإن <span dir="ltr">_D<sub>J</sub>(c,N(i,c[i]))_</span> هي مجموعة وجهات القفز الصالحة التي تبدأ عند رمز التشغيل التالي. إذا لم يكن رمز التشغيل الحالي `JUMPDEST`، فأرجع تلك المجموعة فقط. وإذا كان `JUMPDEST`، فقم بتضمينه في مجموعة النتائج وأرجع ذلك.
## 9.4.4 التوقف الطبيعي {#944-normal-halt}

يمكن لدالة التوقف <span dir="ltr">_H_</span> أن تُرجع ثلاثة أنواع من القيم.

- إذا لم نكن في رمز تشغيل توقف، فأرجع <span dir="ltr">_∅_</span>، المجموعة الفارغة. بالاصطلاح، يتم تفسير هذه القيمة على أنها خطأ منطقي (Boolean false).
- إذا كان لدينا رمز تشغيل توقف لا ينتج مخرجات (إما [`STOP`](https://www.evm.codes/#00) أو [`SELFDESTRUCT`](https://www.evm.codes/#ff))، فأرجع تسلسلًا بحجم صفر بايت كقيمة مرجعة. لاحظ أن هذا يختلف تمامًا عن المجموعة الفارغة. تعني هذه القيمة أن <span dir="ltr">EVM</span> قد توقفت بالفعل، فقط لا توجد بيانات مرجعة لقراءتها.
- إذا كان لدينا رمز تشغيل توقف ينتج مخرجات (إما [`RETURN`](https://www.evm.codes/#f3) أو [`REVERT`](https://www.evm.codes/#fd))، فأرجع تسلسل البايتات المحدد بواسطة رمز التشغيل هذا. يتم أخذ هذا التسلسل من الذاكرة، والقيمة الموجودة في أعلى المكدس (<span dir="ltr">_μ<sub>s</sub>[0]_</span>) هي البايت الأول، والقيمة التي تليها (<span dir="ltr">_μ<sub>s</sub>[1]_</span>) هي الطول.

## H.2 مجموعة التعليمات

قبل أن ننتقل إلى القسم الفرعي الأخير من <span dir="ltr">EVM</span>، <span dir="ltr">9.5</span>، دعونا نلقي نظرة على التعليمات نفسها. تم تعريفها في الملحق H.2 الذي يبدأ في ص <span dir="ltr">30</span>. يُتوقع أن يظل أي شيء لم يتم تحديده على أنه يتغير مع رمز التشغيل المحدد هذا كما هو. يتم تحديد المتغيرات التي تتغير بـ <span dir="ltr">\<something\>′</span>.

على سبيل المثال، دعونا نلقي نظرة على رمز التشغيل [`ADD`](https://www.evm.codes/#01).

| القيمة | الرمز التذكيري | <span dir="ltr">δ</span> | <span dir="ltr">α</span> | الوصف |
| ----: | -------- | --- | --- | --------------------------------------------------------- |
|  <span dir="ltr">0x01</span> | ADD      | <span dir="ltr">2</span>   | <span dir="ltr">1</span>   | عملية الجمع.                                       |
|       |          |     |     | <span dir="ltr">_μ′<sub>s</sub>[0] ≡ μ<sub>s</sub>[0] + μ<sub>s</sub>[1]_</span> |

<span dir="ltr">_δ_</span> هو عدد القيم التي نخرجها من المكدس. في هذه الحالة اثنتان، لأننا نجمع أعلى قيمتين.

<span dir="ltr">_α_</span> هو عدد القيم التي ندخلها مرة أخرى. في هذه الحالة واحدة، وهي المجموع.

لذا فإن أعلى المكدس الجديد (<span dir="ltr">_μ′<sub>s</sub>[0]_</span>) هو مجموع أعلى المكدس القديم (<span dir="ltr">_μ<sub>s</sub>[0]_</span>) والقيمة القديمة أسفله (<span dir="ltr">_μ<sub>s</sub>[1]_</span>).

بدلاً من استعراض جميع رموز التشغيل بقائمة مملة، تشرح هذه المقالة فقط رموز التشغيل التي تقدم شيئًا جديدًا.

| القيمة | الرمز التذكيري  | <span dir="ltr">δ</span>   | <span dir="ltr">α</span>   | الوصف                                                                                                |
| ----: | --------- | --- | --- | ---------------------------------------------------------------------------------------------------------- |
|  <span dir="ltr">0x20</span> | KECCAK256 | <span dir="ltr">2</span>   | <span dir="ltr">1</span>   | حساب تجزئة كيكاك-256.                                                                                   |
|       |           |     |     | <span dir="ltr">_μ′<sub>s</sub>[0] ≡ KEC(μ<sub>m</sub>[μ<sub>s</sub>[0] . . . (μ<sub>s</sub>[0] + μ<sub>s</sub>[1] − 1)])_</span> |
|       |           |     |     | <span dir="ltr">_μ′<sub>i</sub> ≡ M(μ<sub>i</sub>,μ<sub>s</sub>[0],μ<sub>s</sub>[1])_</span>                                      |

هذا هو أول رمز تشغيل يصل إلى الذاكرة (في هذه الحالة، للقراءة فقط). ومع ذلك، قد يتوسع إلى ما هو أبعد من الحدود الحالية للذاكرة، لذا نحتاج إلى تحديث <span dir="ltr">_μ<sub>i</sub>._</span> نقوم بذلك باستخدام الدالة <span dir="ltr">_M_</span> المحددة في المعادلة <span dir="ltr">330</span> في ص <span dir="ltr">30</span>.

| القيمة | الرمز التذكيري | <span dir="ltr">δ</span>   | <span dir="ltr">α</span>   | الوصف                       |
| ----: | -------- | --- | --- | --------------------------------- |
|  <span dir="ltr">0x31</span> | BALANCE  | <span dir="ltr">1</span>   | <span dir="ltr">1</span>   | الحصول على رصيد الحساب المحدد. |
|       |          |     |     | ...                               |

العنوان الذي نحتاج إلى العثور على رصيده هو <span dir="ltr">_μ<sub>s</sub>[0] mod 2<sup>160</sup>_</span>. أعلى المكدس هو العنوان، ولكن نظرًا لأن العناوين تبلغ <span dir="ltr">160 bits</span> فقط، فإننا نحسب القيمة [باقي القسمة (modulo)](https://en.wikipedia.org/wiki/Modulo_operation) <span dir="ltr">2<sup>160</sup></span>.

إذا كان <span dir="ltr">_σ[μ<sub>s</sub>[0] mod 2<sup>160</sup>] ≠ ∅_</span>، فهذا يعني أن هناك معلومات حول هذا العنوان. في هذه الحالة، <span dir="ltr">_σ[μ<sub>s</sub>[0] mod 2<sup>160</sup>]<sub>b</sub>_</span> هو الرصيد لذلك العنوان. إذا كان <span dir="ltr">_σ[μ<sub>s</sub>[0] mod 2<sup>160</sup>] = ∅_</span>، فهذا يعني أن هذا العنوان غير مهيأ والرصيد صفر. يمكنك رؤية قائمة حقول معلومات الحساب في القسم <span dir="ltr">4.1</span> في ص <span dir="ltr">4</span>.

المعادلة الثانية، <span dir="ltr">_A'<sub>a</sub> ≡ A<sub>a</sub> ∪ \{μ<sub>s</sub>[0] mod 2<sup>160</sup>}_</span>، تتعلق بالفرق في التكلفة بين الوصول إلى التخزين الدافئ (التخزين الذي تم الوصول إليه مؤخرًا ومن المحتمل أن يكون مخزنًا مؤقتًا) والتخزين البارد (التخزين الذي لم يتم الوصول إليه ومن المحتمل أن يكون في تخزين أبطأ وأكثر تكلفة في الاسترجاع). <span dir="ltr">_A<sub>a</sub>_</span> هي قائمة العناوين التي تم الوصول إليها مسبقًا بواسطة المعاملة، والتي يجب أن يكون الوصول إليها أرخص، كما هو محدد في القسم <span dir="ltr">6.1</span> في ص <span dir="ltr">9</span>. يمكنك قراءة المزيد حول هذا الموضوع في [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929).

| القيمة | الرمز التذكيري | <span dir="ltr">δ</span>   | <span dir="ltr">α</span>   | الوصف                             |
| ----: | -------- | --- | --- | --------------------------------------- |
|  <span dir="ltr">0x8F</span> | DUP16    | <span dir="ltr">16</span>  | <span dir="ltr">17</span>  | تكرار عنصر المكدس السادس عشر.              |
|       |          |     |     | <span dir="ltr">_μ′<sub>s</sub>[0] ≡ μ<sub>s</sub>[15]_</span> |

لاحظ أنه لاستخدام أي عنصر في المكدس، نحتاج إلى إخراجه، مما يعني أننا نحتاج أيضًا إلى إخراج جميع عناصر المكدس الموجودة فوقه. في حالة [`DUP<n>`](https://www.evm.codes/#8f) و [`SWAP<n>`](https://www.evm.codes/#9f)، يعني هذا الاضطرار إلى إخراج ثم إدخال ما يصل إلى ستة عشر قيمة.
## 9.5 دورة التنفيذ

الآن بعد أن أصبح لدينا جميع الأجزاء، يمكننا أخيرًا فهم كيفية توثيق دورة تنفيذ <span dir="ltr">EVM</span>.

تنص المعادلة (<span dir="ltr">164</span>) على أنه بالنظر إلى الحالة:

- <span dir="ltr">_σ_</span> (حالة سلسلة الكتل العالمية)
- <span dir="ltr">_μ_</span> (حالة <span dir="ltr">EVM</span>)
- <span dir="ltr">_A_</span> (الحالة الفرعية، التغييرات التي ستحدث عند انتهاء المعاملة)
- <span dir="ltr">_I_</span> (بيئة التنفيذ)

الحالة الجديدة هي <span dir="ltr">_(σ', μ', A', I')_</span>.

تحدد المعادلات (<span dir="ltr">165</span>)-(<span dir="ltr">167</span>) المكدس والتغيير فيه بسبب رمز التشغيل (<span dir="ltr">_μ<sub>s</sub>_</span>). المعادلة (<span dir="ltr">168</span>) هي التغيير في الغاز (<span dir="ltr">_μ<sub>g</sub>_</span>). المعادلة (<span dir="ltr">169</span>) هي التغيير في عداد البرنامج (<span dir="ltr">_μ<sub>pc</sub>_</span>). أخيرًا، تحدد المعادلات (<span dir="ltr">170</span>)-(<span dir="ltr">173</span>) أن المعلمات الأخرى تظل كما هي، ما لم يتم تغييرها صراحةً بواسطة رمز التشغيل.

وبهذا يتم تعريف <span dir="ltr">EVM</span> بالكامل.
## الخاتمة {#conclusion}

التدوين الرياضي دقيق وقد سمح للورقة الصفراء بتحديد كل تفاصيل إيثيريوم. ومع ذلك، فإن لها بعض العيوب:

- لا يمكن فهمها إلا من قبل البشر، مما يعني أنه يجب كتابة [اختبارات الامتثال](https://github.com/ethereum/tests) يدويًا.
- يفهم المبرمجون كود الحاسوب.
  قد يفهمون أو لا يفهمون التدوين الرياضي.

ربما لهذه الأسباب، تمت كتابة [مواصفات طبقة الإجماع](https://github.com/ethereum/consensus-specs/blob/master/tests/core/pyspec/README.md) الأحدث بلغة Python. هناك [مواصفات طبقة التنفيذ بلغة Python](https://ethereum.github.io/execution-specs)، لكنها غير مكتملة. حتى وما لم تتم ترجمة الورقة الصفراء بأكملها أيضًا إلى Python أو لغة مشابهة، ستستمر الورقة الصفراء في الخدمة، ومن المفيد أن تكون قادرًا على قراءتها.
