ইয়েলো পেপারের EVM স্পেসিফিকেশন বোঝা
ইয়েলো পেপার (নতুন ট্যাবে খোলে) হলো ইথেরিয়ামের আনুষ্ঠানিক স্পেসিফিকেশন। EIP প্রক্রিয়া দ্বারা সংশোধিত স্থানগুলো ছাড়া, এটি সবকিছু কীভাবে কাজ করে তার সঠিক বিবরণ ধারণ করে। এটি একটি গাণিতিক পেপার হিসেবে লেখা হয়েছে, যার মধ্যে এমন পরিভাষা রয়েছে যা প্রোগ্রামারদের কাছে পরিচিত নাও হতে পারে। এই পেপারে আপনি শিখবেন কীভাবে এটি পড়তে হয় এবং এর পাশাপাশি অন্যান্য সম্পর্কিত গাণিতিক পেপারগুলোও পড়তে হয়।
কোন ইয়েলো পেপার?
ইথেরিয়ামের প্রায় অন্য সবকিছুর মতো, ইয়েলো পেপারও সময়ের সাথে সাথে বিবর্তিত হয়। একটি নির্দিষ্ট সংস্করণ উল্লেখ করতে সক্ষম হওয়ার জন্য, আমি লেখার সময়কার বর্তমান সংস্করণটি (নতুন ট্যাবে খোলে) আপলোড করেছি। আমি যে বিভাগ, পৃষ্ঠা এবং সমীকরণ নম্বরগুলো ব্যবহার করব তা ওই সংস্করণটিকে নির্দেশ করবে। এই ডকুমেন্টটি পড়ার সময় এটি অন্য একটি উইন্ডোতে খোলা রাখা ভালো।
EVM কেন?
মূল ইয়েলো পেপারটি ইথেরিয়ামের বিকাশের একেবারে শুরুতেই লেখা হয়েছিল। এটি মূল প্রুফ-অফ-ওয়ার্ক (PoW) ভিত্তিক কনসেনসাস মেকানিজম বর্ণনা করে যা প্রাথমিকভাবে নেটওয়ার্ক সুরক্ষিত করতে ব্যবহৃত হয়েছিল। যাইহোক, ইথেরিয়াম প্রুফ-অফ-ওয়ার্ক বন্ধ করে দেয় এবং 2022 সালের সেপ্টেম্বরে প্রুফ-অফ-স্টেক (PoS) ভিত্তিক ঐক্যমত ব্যবহার শুরু করে। এই টিউটোরিয়ালটি ইয়েলো পেপারের সেই অংশগুলোর ওপর ফোকাস করবে যা ইথেরিয়াম ভার্চুয়াল মেশিনকে সংজ্ঞায়িত করে। প্রুফ-অফ-স্টেক-এ রূপান্তরের ফলে EVM অপরিবর্তিত ছিল (DIFFICULTY অপকোডের রিটার্ন ভ্যালু ছাড়া)।
9 এক্সিকিউশন মডেল
এই বিভাগে (পৃষ্ঠা 14-16) EVM-এর বেশিরভাগ সংজ্ঞা অন্তর্ভুক্ত রয়েছে।
সিস্টেম স্টেট (system state) শব্দটির মধ্যে সিস্টেমটি চালানোর জন্য আপনার যা কিছু জানা দরকার তার সবকিছু অন্তর্ভুক্ত থাকে। একটি সাধারণ কম্পিউটারে, এর অর্থ হলো মেমরি, রেজিস্টারের বিষয়বস্তু ইত্যাদি।
একটি টুরিং মেশিন (নতুন ট্যাবে খোলে) হলো একটি কম্পিউটেশনাল মডেল। মূলত, এটি একটি কম্পিউটারের সরলীকৃত সংস্করণ, যা প্রমাণ করে যে একটি সাধারণ কম্পিউটার যে গণনাগুলো চালাতে পারে, এটিও তা করতে সক্ষম (একটি কম্পিউটার যা কিছু গণনা করতে পারে, একটি টুরিং মেশিনও তা গণনা করতে পারে এবং এর বিপরীতটিও সত্য)। এই মডেলটি কী গণনাযোগ্য এবং কী নয় সে সম্পর্কে বিভিন্ন উপপাদ্য প্রমাণ করা সহজ করে তোলে।
টুরিং-কমপ্লিট (নতুন ট্যাবে খোলে) (Turing-complete) শব্দটির অর্থ হলো এমন একটি কম্পিউটার যা টুরিং মেশিনের মতো একই গণনা চালাতে পারে। টুরিং মেশিনগুলো অসীম লুপে (infinite loops) প্রবেশ করতে পারে, কিন্তু EVM তা পারে না কারণ এর গ্যাস শেষ হয়ে যাবে, তাই এটি কেবল কোয়াসি-টুরিং-কমপ্লিট (quasi-Turing-complete)।
9.1 বেসিকস
এই বিভাগটি EVM-এর বেসিকস এবং অন্যান্য কম্পিউটেশনাল মডেলের সাথে এর তুলনা কীভাবে করা যায় তা তুলে ধরে।
একটি স্ট্যাক মেশিন (নতুন ট্যাবে খোলে) হলো এমন একটি কম্পিউটার যা মধ্যবর্তী ডেটা রেজিস্টারে নয়, বরং একটি স্ট্যাকে (নতুন ট্যাবে খোলে) সংরক্ষণ করে। এটি ভার্চুয়াল মেশিনগুলোর জন্য পছন্দের আর্কিটেকচার কারণ এটি বাস্তবায়ন করা সহজ, যার অর্থ হলো বাগ এবং নিরাপত্তা দুর্বলতার সম্ভাবনা অনেক কম। স্ট্যাকের মেমরি 256-বিট শব্দে (words) বিভক্ত। এটি বেছে নেওয়া হয়েছিল কারণ এটি ইথেরিয়ামের মূল ক্রিপ্টোগ্রাফিক ক্রিয়াকলাপ যেমন কেক্যাক-২৫৬ হ্যাশিং এবং উপবৃত্তাকার বক্ররেখা গণনার জন্য সুবিধাজনক। স্ট্যাকের সর্বোচ্চ আকার হলো 1024টি আইটেম (1024 x 256 বিট)। যখন অপকোডগুলো এক্সিকিউট করা হয় তখন তারা সাধারণত স্ট্যাক থেকে তাদের প্যারামিটারগুলো পায়। স্ট্যাকের উপাদানগুলো পুনর্গঠন করার জন্য বিশেষভাবে অপকোড রয়েছে যেমন POP (স্ট্যাকের শীর্ষ থেকে আইটেম সরিয়ে দেয়), DUP_N (স্ট্যাকের N-তম আইটেম ডুপ্লিকেট করে), ইত্যাদি।
EVM-এ মেমরি নামক একটি ভোলাটাইল স্পেসও রয়েছে যা এক্সিকিউশনের সময় ডেটা সংরক্ষণ করতে ব্যবহৃত হয়। এই মেমরিটি 32-বাইট শব্দে সংগঠিত। সমস্ত মেমরি লোকেশন শূন্যতে (zero) ইনিশিয়ালাইজ করা হয়। আপনি যদি মেমরিতে একটি শব্দ যোগ করতে এই Yul (নতুন ট্যাবে খোলে) কোডটি এক্সিকিউট করেন, তবে এটি শব্দের খালি জায়গাটি শূন্য দিয়ে পূরণ করে 32 বাইট মেমরি পূর্ণ করবে, অর্থাৎ, এটি একটি শব্দ তৈরি করে - যার 0-29 লোকেশনে শূন্য, 30-এ 0x60 এবং 31-এ 0xA7 থাকে।
mstore(0, 0x60A7)
mstore হলো মেমরির সাথে ইন্টারঅ্যাক্ট করার জন্য EVM দ্বারা প্রদত্ত তিনটি অপকোডের মধ্যে একটি - এটি মেমরিতে একটি শব্দ লোড করে। অন্য দুটি হলো mstore8 যা মেমরিতে একটি একক বাইট লোড করে এবং mload যা মেমরি থেকে স্ট্যাকে একটি শব্দ স্থানান্তর করে।
EVM-এর একটি পৃথক নন-ভোলাটাইল স্টোরেজ মডেলও রয়েছে যা সিস্টেম স্টেটের অংশ হিসেবে রক্ষণাবেক্ষণ করা হয় - এই মেমরিটি ওয়ার্ড অ্যারেতে (স্ট্যাকের ওয়ার্ড-অ্যাড্রেসেবল বাইট অ্যারের বিপরীতে) সংগঠিত হয়। এই স্টোরেজেই কন্ট্রাক্টগুলো স্থায়ী ডেটা রাখে - একটি কন্ট্রাক্ট কেবল তার নিজস্ব স্টোরেজের সাথেই ইন্টারঅ্যাক্ট করতে পারে। স্টোরেজ কী-ভ্যালু (key-value) ম্যাপিংয়ে সংগঠিত হয়।
যদিও ইয়েলো পেপারের এই বিভাগে এটি উল্লেখ করা হয়নি, তবে এটি জানাও দরকারী যে চতুর্থ ধরণের একটি মেমরি রয়েছে। কল ডেটা (Calldata) হলো বাইট-অ্যাড্রেসেবল রিড-অনলি মেমরি যা একটি ট্রানজ্যাকশনের data প্যারামিটারের সাথে পাস করা মান সংরক্ষণ করতে ব্যবহৃত হয়। calldata পরিচালনা করার জন্য EVM-এর নির্দিষ্ট অপকোড রয়েছে। calldatasize ডেটার আকার রিটার্ন করে। calldataload ডেটা স্ট্যাকে লোড করে। calldatacopy ডেটা মেমরিতে কপি করে।
স্ট্যান্ডার্ড ভন নিউম্যান আর্কিটেকচার (নতুন ট্যাবে খোলে) কোড এবং ডেটা একই মেমরিতে সংরক্ষণ করে। নিরাপত্তার কারণে EVM এই স্ট্যান্ডার্ড অনুসরণ করে না - ভোলাটাইল মেমরি শেয়ার করার ফলে প্রোগ্রাম কোড পরিবর্তন করা সম্ভব হয়। এর পরিবর্তে, কোড স্টোরেজে সেভ করা হয়।
কেবলমাত্র দুটি ক্ষেত্রে মেমরি থেকে কোড এক্সিকিউট করা হয়:
- যখন একটি কন্ট্রাক্ট অন্য একটি কন্ট্রাক্ট তৈরি করে (
CREATE(নতুন ট্যাবে খোলে) বাCREATE2(নতুন ট্যাবে খোলে) ব্যবহার করে), তখন কন্ট্রাক্ট কনস্ট্রাক্টরের কোড মেমরি থেকে আসে। - যেকোনো কন্ট্রাক্ট তৈরির সময়, কনস্ট্রাক্টর কোড রান করে এবং তারপর আসল কন্ট্রাক্টের কোড নিয়ে রিটার্ন করে, যা মেমরি থেকেও আসে।
অস্বাভাবিক এক্সিকিউশন (exceptional execution) বলতে এমন একটি ব্যতিক্রমকে বোঝায় যার কারণে বর্তমান কন্ট্রাক্টের এক্সিকিউশন থেমে যায়।
9.2 ফি ওভারভিউ
এই বিভাগে গ্যাস ফি কীভাবে গণনা করা হয় তা ব্যাখ্যা করা হয়েছে। এর তিনটি খরচ রয়েছে:
অপকোড খরচ
নির্দিষ্ট অপকোডের অন্তর্নিহিত খরচ। এই মানটি পেতে, পরিশিষ্ট H-এ (পৃষ্ঠা 29, সমীকরণ (329)-এর অধীনে) অপকোডের কস্ট গ্রুপটি খুঁজুন এবং সমীকরণ (326)-এ কস্ট গ্রুপটি খুঁজুন। এটি আপনাকে একটি কস্ট ফাংশন দেয়, যা বেশিরভাগ ক্ষেত্রে পরিশিষ্ট G (পৃষ্ঠা 28) থেকে প্যারামিটারগুলো ব্যবহার করে।
উদাহরণস্বরূপ, CALLDATACOPY (নতুন ট্যাবে খোলে) অপকোডটি Wcopy গ্রুপের একটি সদস্য। সেই গ্রুপের জন্য অপকোড খরচ হলো Gverylow+Gcopy×⌈μs[2]÷32⌉। পরিশিষ্ট G-এর দিকে তাকালে, আমরা দেখতে পাই যে উভয় ধ্রুবকই 3, যা আমাদের দেয় 3+3×⌈μs[2]÷32⌉।
আমাদের এখনও ⌈μs[2]÷32⌉ এক্সপ্রেশনটির অর্থ বের করতে হবে। সবচেয়ে বাইরের অংশ, ⌈ <value> ⌉ হলো সিলিং ফাংশন (ceiling function), এমন একটি ফাংশন যা একটি মান দেওয়া হলে সবচেয়ে ছোট পূর্ণসংখ্যা রিটার্ন করে যা এখনও মানটির চেয়ে ছোট নয়। উদাহরণস্বরূপ, ⌈2.5⌉ = ⌈3⌉ = 3। ভেতরের অংশটি হলো μs[2]÷32। পৃষ্ঠা 3-এ বিভাগ 3 (Conventions)-এর দিকে তাকালে, μ হলো মেশিন স্টেট। মেশিন স্টেটটি পৃষ্ঠা 15-এর 9.4.1 বিভাগে সংজ্ঞায়িত করা হয়েছে। সেই বিভাগ অনুসারে, মেশিন স্টেটের একটি প্যারামিটার হলো স্ট্যাকের জন্য s। সবকিছু একসাথে রাখলে, মনে হয় যে μs[2] হলো স্ট্যাকের #2 লোকেশন। অপকোডটির (নতুন ট্যাবে খোলে) দিকে তাকালে, স্ট্যাকের #2 লোকেশন হলো বাইটে ডেটার আকার। Wcopy গ্রুপের অন্যান্য অপকোড, CODECOPY (নতুন ট্যাবে খোলে) এবং RETURNDATACOPY (নতুন ট্যাবে খোলে)-এর দিকে তাকালে, তাদেরও একই লোকেশনে ডেটার আকার রয়েছে। সুতরাং ⌈μs[2]÷32⌉ হলো কপি করা ডেটা সংরক্ষণ করার জন্য প্রয়োজনীয় 32 বাইট শব্দের সংখ্যা। সবকিছু একসাথে রাখলে, CALLDATACOPY (নতুন ট্যাবে খোলে)-এর অন্তর্নিহিত খরচ হলো 3 গ্যাস এবং কপি করা ডেটার প্রতি শব্দের জন্য 3 গ্যাস।
রানিং খরচ
আমরা যে কোডটি কল করছি তা চালানোর খরচ।
CREATE(নতুন ট্যাবে খোলে) এবংCREATE2(নতুন ট্যাবে খোলে)-এর ক্ষেত্রে, নতুন কন্ট্রাক্টের জন্য কনস্ট্রাক্টর।CALL(নতুন ট্যাবে খোলে),CALLCODE(নতুন ট্যাবে খোলে),STATICCALL(নতুন ট্যাবে খোলে), বাDELEGATECALL(নতুন ট্যাবে খোলে)-এর ক্ষেত্রে, আমরা যে কন্ট্রাক্টটি কল করি।
মেমরি সম্প্রসারণের খরচ
মেমরি সম্প্রসারণের খরচ (যদি প্রয়োজন হয়)।
সমীকরণ 326-এ, এই মানটি Cmem(μi')-Cmem(μi) হিসেবে লেখা হয়েছে। আবার 9.4.1 বিভাগের দিকে তাকালে, আমরা দেখতে পাই যে μi হলো মেমরিতে শব্দের সংখ্যা। সুতরাং μi হলো অপকোডের আগে মেমরিতে শব্দের সংখ্যা এবং μi' হলো অপকোডের পরে মেমরিতে শব্দের সংখ্যা।
Cmem ফাংশনটি সমীকরণ 328-এ সংজ্ঞায়িত করা হয়েছে: Cmem(a) = Gmemory × a + ⌊a2 ÷ 512⌋। ⌊x⌋ হলো ফ্লোর ফাংশন (floor function), এমন একটি ফাংশন যা একটি মান দেওয়া হলে সবচেয়ে বড় পূর্ণসংখ্যা রিটার্ন করে যা এখনও মানটির চেয়ে বড় নয়। উদাহরণস্বরূপ, ⌊2.5⌋ = ⌊2⌋ = 2। যখন a < √512, a2 < 512, এবং ফ্লোর ফাংশনের ফলাফল শূন্য হয়। সুতরাং প্রথম 22টি শব্দের (704 বাইট) জন্য, প্রয়োজনীয় মেমরি শব্দের সংখ্যার সাথে খরচ রৈখিকভাবে বৃদ্ধি পায়। সেই বিন্দুর পরে ⌊a2 ÷ 512⌋ ধনাত্মক হয়। যখন প্রয়োজনীয় মেমরি যথেষ্ট বেশি হয়, তখন গ্যাস খরচ মেমরির পরিমাণের বর্গের সমানুপাতিক হয়।
মনে রাখবেন যে এই বিষয়গুলো কেবল অন্তর্নিহিত গ্যাস খরচকে প্রভাবিত করে - এটি ফি মার্কেট বা ভ্যালিডেটরদের দেওয়া টিপস বিবেচনা করে না যা নির্ধারণ করে যে একজন শেষ ব্যবহারকারীকে কত টাকা দিতে হবে - এটি কেবল EVM-এ একটি নির্দিষ্ট অপারেশন চালানোর কাঁচা খরচ।
9.3 এক্সিকিউশন এনভায়রনমেন্ট
এক্সিকিউশন এনভায়রনমেন্ট হলো একটি টুপল (tuple), I, যার মধ্যে এমন তথ্য অন্তর্ভুক্ত থাকে যা ব্লকচেইন স্টেট বা EVM-এর অংশ নয়।
| প্যারামিটার | ডেটা অ্যাক্সেস করার জন্য অপকোড | ডেটা অ্যাক্সেস করার জন্য Solidity কোড |
|---|---|---|
| Ia | ADDRESS (নতুন ট্যাবে খোলে) | address(this) |
| Io | ORIGIN (নতুন ট্যাবে খোলে) | tx.origin |
| Ip | GASPRICE (নতুন ট্যাবে খোলে) | tx.gasprice |
| Id | CALLDATALOAD (নতুন ট্যাবে খোলে), ইত্যাদি | msg.data |
| Is | CALLER (নতুন ট্যাবে খোলে) | msg.sender |
| Iv | CALLVALUE (নতুন ট্যাবে খোলে) | msg.value |
| Ib | CODECOPY (নতুন ট্যাবে খোলে) | address(this).code |
| IH | ব্লক হেডার ফিল্ড, যেমন NUMBER (নতুন ট্যাবে খোলে) এবং DIFFICULTY (নতুন ট্যাবে খোলে) | block.number, block.difficulty, ইত্যাদি |
| Ie | কন্ট্রাক্টগুলোর মধ্যে কলের জন্য কল স্ট্যাকের গভীরতা (কন্ট্রাক্ট তৈরি সহ) | |
| Iw | EVM-কে কি স্টেট পরিবর্তন করার অনুমতি দেওয়া হয়েছে, নাকি এটি স্ট্যাটিকভাবে চলছে |
বিভাগ 9-এর বাকি অংশ বোঝার জন্য আরও কয়েকটি প্যারামিটার প্রয়োজনীয়:
| প্যারামিটার | যে বিভাগে সংজ্ঞায়িত | অর্থ |
|---|---|---|
| σ | 2 (পৃষ্ঠা 2, সমীকরণ 1) | ব্লকচেইনের স্টেট |
| g | 9.3 (পৃষ্ঠা 14) | অবশিষ্ট গ্যাস |
| A | 6.1 (পৃষ্ঠা 9) | অ্যাক্রুড সাবস্টেট (ট্রানজ্যাকশন শেষ হওয়ার জন্য নির্ধারিত পরিবর্তনগুলো) |
| o | 9.3 (পৃষ্ঠা 14) | আউটপুট - অভ্যন্তরীণ লেনদেনের ক্ষেত্রে রিটার্ন করা ফলাফল (যখন একটি কন্ট্রাক্ট অন্যটিকে কল করে) এবং ভিউ ফাংশনগুলোতে কল (যখন আপনি কেবল তথ্যের জন্য জিজ্ঞাসা করছেন, তাই ট্রানজ্যাকশনের জন্য অপেক্ষা করার দরকার নেই) |
9.4 এক্সিকিউশন ওভারভিউ
এখন যেহেতু আমাদের কাছে সমস্ত প্রাথমিক বিষয়গুলো রয়েছে, আমরা অবশেষে EVM কীভাবে কাজ করে তা নিয়ে কাজ শুরু করতে পারি।
সমীকরণ 146-151 আমাদের EVM চালানোর জন্য প্রাথমিক শর্তগুলো দেয়:
| প্রতীক | প্রাথমিক মান | অর্থ |
|---|---|---|
| μg | g | অবশিষ্ট গ্যাস |
| μpc | 0 | প্রোগ্রাম কাউন্টার, এক্সিকিউট করার জন্য পরবর্তী নির্দেশনার ঠিকানা |
| μm | (0, 0, ...) | মেমরি, সব শূন্যতে ইনিশিয়ালাইজ করা হয়েছে |
| μi | 0 | ব্যবহৃত সর্বোচ্চ মেমরি লোকেশন |
| μs | () | স্ট্যাক, প্রাথমিকভাবে খালি |
| μo | ∅ | আউটপুট, খালি সেট যতক্ষণ না আমরা রিটার্ন ডেটা সহ (RETURN (নতুন ট্যাবে খোলে) বা REVERT (নতুন ট্যাবে খোলে)) অথবা এটি ছাড়া (STOP (নতুন ট্যাবে খোলে) বা SELFDESTRUCT (নতুন ট্যাবে খোলে)) থামি। |
সমীকরণ 152 আমাদের বলে যে এক্সিকিউশনের সময় প্রতিটি বিন্দুতে চারটি সম্ভাব্য শর্ত রয়েছে এবং সেগুলোর সাথে কী করতে হবে:
Z(σ,μ,A,I)। Z এমন একটি ফাংশনকে উপস্থাপন করে যা পরীক্ষা করে যে কোনো অপারেশন একটি অবৈধ স্টেট ট্রানজিশন তৈরি করে কিনা (অস্বাভাবিক হল্টিং দেখুন)। যদি এটি True হিসেবে মূল্যায়িত হয়, তবে নতুন স্টেটটি পুরানোটির মতোই থাকে (গ্যাস পোড়ানো ছাড়া) কারণ পরিবর্তনগুলো বাস্তবায়িত হয়নি。- যদি এক্সিকিউট করা অপকোডটি
REVERT(নতুন ট্যাবে খোলে) হয়, তবে নতুন স্টেটটি পুরানো স্টেটের মতোই থাকে, কিছু গ্যাস নষ্ট হয়। - যদি অপারেশনগুলোর সিকোয়েন্স শেষ হয়ে যায়, যা একটি
RETURN(নতুন ট্যাবে খোলে) দ্বারা নির্দেশিত হয়, তবে স্টেটটি নতুন স্টেটে আপডেট করা হয়। - যদি আমরা 1-3 শেষ শর্তগুলোর কোনোটিতে না থাকি, তবে চালানো চালিয়ে যান।
9.4.1 মেশিন স্টেট
এই বিভাগে মেশিন স্টেট সম্পর্কে আরও বিস্তারিতভাবে ব্যাখ্যা করা হয়েছে। এটি নির্দিষ্ট করে যে w হলো বর্তমান অপকোড। যদি μpc কোডের দৈর্ঘ্য ||Ib||-এর চেয়ে কম হয়, তবে সেই বাইটটি (Ib[μpc]) হলো অপকোড। অন্যথায়, অপকোডটিকে STOP (নতুন ট্যাবে খোলে) হিসেবে সংজ্ঞায়িত করা হয়।
যেহেতু এটি একটি স্ট্যাক মেশিন (নতুন ট্যাবে খোলে), তাই আমাদের প্রতিটি অপকোড দ্বারা পপ আউট (δ) এবং পুশ ইন (α) করা আইটেমগুলোর সংখ্যার ট্র্যাক রাখতে হবে।
9.4.2 অস্বাভাবিক হল্টিং
এই বিভাগটি Z ফাংশনকে সংজ্ঞায়িত করে, যা নির্দিষ্ট করে কখন আমাদের একটি অস্বাভাবিক সমাপ্তি (abnormal termination) ঘটে। এটি একটি বুলিয়ান (নতুন ট্যাবে খোলে) ফাংশন, তাই এটি লজিক্যাল or-এর জন্য ∨ (নতুন ট্যাবে খোলে) এবং লজিক্যাল and-এর জন্য ∧ (নতুন ট্যাবে খোলে) ব্যবহার করে।
এই শর্তগুলোর কোনোটি সত্য হলে আমাদের একটি অস্বাভাবিক হল্ট (exceptional halt) ঘটে:
-
μg < C(σ,μ,A,I) যেমনটি আমরা 9.2 বিভাগে দেখেছি, C হলো সেই ফাংশন যা গ্যাস খরচ নির্দিষ্ট করে। পরবর্তী অপকোড কভার করার জন্য পর্যাপ্ত গ্যাস অবশিষ্ট নেই।
-
δw=∅ যদি কোনো অপকোডের জন্য পপ করা আইটেমগুলোর সংখ্যা অনির্ধারিত হয়, তবে অপকোডটি নিজেই অনির্ধারিত।
-
|| μs || < δw স্ট্যাক আন্ডারফ্লো, বর্তমান অপকোডের জন্য স্ট্যাকে পর্যাপ্ত আইটেম নেই।
-
w = JUMP ∧ μs[0]∉D(Ib) অপকোডটি হলো
JUMP(নতুন ট্যাবে খোলে) এবং ঠিকানাটি একটিJUMPDEST(নতুন ট্যাবে খোলে) নয়। জাম্পগুলো কেবলমাত্র তখনই বৈধ যখন গন্তব্যটি একটিJUMPDEST(নতুন ট্যাবে খোলে) হয়। -
w = JUMPI ∧ μs[1]≠0 ∧ μs[0] ∉ D(Ib) অপকোডটি হলো
JUMPI(নতুন ট্যাবে খোলে), শর্তটি সত্য (শূন্য নয়) তাই জাম্প হওয়া উচিত, এবং ঠিকানাটি একটিJUMPDEST(নতুন ট্যাবে খোলে) নয়। জাম্পগুলো কেবলমাত্র তখনই বৈধ যখন গন্তব্যটি একটিJUMPDEST(নতুন ট্যাবে খোলে) হয়। -
w = RETURNDATACOPY ∧ μs[1]+μs[2]>|| μo || অপকোডটি হলো
RETURNDATACOPY(নতুন ট্যাবে খোলে)। এই অপকোডে স্ট্যাক উপাদান μs[1] হলো রিটার্ন ডেটা বাফার থেকে পড়ার জন্য অফসেট, এবং স্ট্যাক উপাদান μs[2] হলো ডেটার দৈর্ঘ্য। এই শর্তটি তখন ঘটে যখন আপনি রিটার্ন ডেটা বাফারের শেষের বাইরে পড়ার চেষ্টা করেন। মনে রাখবেন যে কল ডেটা বা কোডের জন্য অনুরূপ কোনো শর্ত নেই। যখন আপনি সেই বাফারগুলোর শেষের বাইরে পড়ার চেষ্টা করেন তখন আপনি কেবল শূন্য পান। -
|| μs || - δw + αw > 1024
স্ট্যাক ওভারফ্লো। যদি অপকোডটি চালানোর ফলে 1024টির বেশি আইটেমের একটি স্ট্যাক তৈরি হয়, তবে বাতিল করুন।
-
¬Iw ∧ W(w,μ) আমরা কি স্ট্যাটিকভাবে চলছি (¬ হলো নেগেশন (নতুন ট্যাবে খোলে) এবং Iw সত্য হয় যখন আমাদের ব্লকচেইন স্টেট পরিবর্তন করার অনুমতি দেওয়া হয়)? যদি তাই হয়, এবং আমরা একটি স্টেট পরিবর্তনকারী অপারেশন করার চেষ্টা করছি, তবে এটি ঘটতে পারে না।
W(w,μ) ফাংশনটি পরে সমীকরণ 159-এ সংজ্ঞায়িত করা হয়েছে। W(w,μ) সত্য হয় যদি এই শর্তগুলোর কোনোটি সত্য হয়:
-
w ∈ {CREATE, CREATE2, SSTORE, SELFDESTRUCT} এই অপকোডগুলো একটি নতুন কন্ট্রাক্ট তৈরি করে, একটি মান সংরক্ষণ করে, বা বর্তমান কন্ট্রাক্টটি ধ্বংস করে স্টেট পরিবর্তন করে।
-
LOG0≤w ∧ w≤LOG4 যদি আমাদের স্ট্যাটিকভাবে কল করা হয় তবে আমরা লগ এন্ট্রিগুলো নির্গত করতে পারি না। লগ অপকোডগুলো সবই
LOG0(A0) (নতুন ট্যাবে খোলে) এবংLOG4(A4) (নতুন ট্যাবে খোলে)-এর মধ্যবর্তী রেঞ্জে রয়েছে। লগ অপকোডের পরের সংখ্যাটি নির্দিষ্ট করে যে লগ এন্ট্রিতে কতগুলো টপিক রয়েছে। -
w=CALL ∧ μs[2]≠0 আপনি স্ট্যাটিক থাকা অবস্থায় অন্য একটি কন্ট্রাক্ট কল করতে পারেন, তবে আপনি যদি তা করেন তবে আপনি এতে ETH হস্তান্তর করতে পারবেন না।
-
-
w = SSTORE ∧ μg ≤ Gcallstipend আপনার কাছে Gcallstipend (পরিশিষ্ট G-এ 2300 হিসেবে সংজ্ঞায়িত) গ্যাসের বেশি না থাকলে আপনি
SSTORE(নতুন ট্যাবে খোলে) চালাতে পারবেন না।
9.4.3 জাম্প ডেস্টিনেশন ভ্যালিডিটি (Jump Destination Validity)
এখানে আমরা আনুষ্ঠানিকভাবে সংজ্ঞায়িত করি যে JUMPDEST (নতুন ট্যাবে খোলে) অপকোডগুলো কী। আমরা কেবল বাইট ভ্যালু 0x5B খুঁজতে পারি না, কারণ এটি একটি PUSH-এর ভিতরে থাকতে পারে (এবং তাই এটি ডেটা, অপকোড নয়)।
সমীকরণ (162)-এ আমরা একটি ফাংশন, N(i,w) সংজ্ঞায়িত করি। প্রথম প্যারামিটার, i, হলো অপকোডের অবস্থান। দ্বিতীয়টি, w, হলো অপকোড নিজেই। যদি w∈[PUSH1, PUSH32] হয়, তবে এর অর্থ হলো অপকোডটি একটি PUSH (স্কোয়ার ব্র্যাকেট এমন একটি রেঞ্জ সংজ্ঞায়িত করে যার মধ্যে প্রান্তবিন্দুগুলো অন্তর্ভুক্ত থাকে)। সেই ক্ষেত্রে পরবর্তী অপকোডটি i+2+(w−PUSH1)-এ থাকে। PUSH1 (নতুন ট্যাবে খোলে)-এর জন্য আমাদের 2 বাইট (PUSH নিজেই এবং 1 বাইট ভ্যালু) এগিয়ে যেতে হবে, PUSH2 (নতুন ট্যাবে খোলে)-এর জন্য আমাদের 3 বাইট এগিয়ে যেতে হবে কারণ এটি একটি 2 বাইট ভ্যালু, ইত্যাদি। অন্যান্য সমস্ত EVM অপকোড কেবল 1 বাইট দীর্ঘ, তাই অন্যান্য সমস্ত ক্ষেত্রে N(i,w)=i+1।
এই ফাংশনটি সমীকরণ (161)-এ DJ(c,i) সংজ্ঞায়িত করতে ব্যবহৃত হয়, যা হলো কোড c-এ সমস্ত বৈধ জাম্প ডেস্টিনেশনের সেট (নতুন ট্যাবে খোলে), যা অপকোড অবস্থান i থেকে শুরু হয়। এই ফাংশনটি রিকার্সিভভাবে সংজ্ঞায়িত করা হয়েছে। যদি i≥||c|| হয়, তবে এর অর্থ হলো আমরা কোডের শেষে বা তার পরে আছি। আমরা আর কোনো জাম্প ডেস্টিনেশন খুঁজে পাব না, তাই কেবল খালি সেট রিটার্ন করুন।
অন্যান্য সমস্ত ক্ষেত্রে আমরা পরবর্তী অপকোডে গিয়ে এবং সেখান থেকে শুরু হওয়া সেটটি পেয়ে কোডের বাকি অংশটি দেখি। c[i] হলো বর্তমান অপকোড, তাই N(i,c[i]) হলো পরবর্তী অপকোডের অবস্থান। তাই DJ(c,N(i,c[i])) হলো বৈধ জাম্প ডেস্টিনেশনের সেট যা পরবর্তী অপকোড থেকে শুরু হয়। যদি বর্তমান অপকোডটি JUMPDEST না হয়, তবে কেবল সেই সেটটি রিটার্ন করুন। যদি এটি JUMPDEST হয়, তবে এটিকে রেজাল্ট সেটে অন্তর্ভুক্ত করুন এবং সেটি রিটার্ন করুন।
9.4.4 স্বাভাবিক হল্টিং
হল্টিং ফাংশন H, তিন ধরণের মান রিটার্ন করতে পারে।
- যদি আমরা কোনো হল্ট অপকোডে না থাকি, তবে ∅, খালি সেট রিটার্ন করুন। প্রথা অনুযায়ী, এই মানটিকে বুলিয়ান ফলস (false) হিসেবে ব্যাখ্যা করা হয়।
- যদি আমাদের এমন একটি হল্ট অপকোড থাকে যা আউটপুট তৈরি করে না (
STOP(নতুন ট্যাবে খোলে) বাSELFDESTRUCT(নতুন ট্যাবে খোলে)), তবে রিটার্ন ভ্যালু হিসেবে শূন্য বাইট আকারের একটি সিকোয়েন্স রিটার্ন করুন। মনে রাখবেন যে এটি খালি সেটের চেয়ে খুব আলাদা। এই মানের অর্থ হলো EVM সত্যিই থেমে গেছে, কেবল পড়ার মতো কোনো রিটার্ন ডেটা নেই। - যদি আমাদের এমন একটি হল্ট অপকোড থাকে যা আউটপুট তৈরি করে (
RETURN(নতুন ট্যাবে খোলে) বাREVERT(নতুন ট্যাবে খোলে)), তবে সেই অপকোড দ্বারা নির্দিষ্ট করা বাইটের সিকোয়েন্স রিটার্ন করুন। এই সিকোয়েন্সটি মেমরি থেকে নেওয়া হয়, স্ট্যাকের শীর্ষের মানটি (μs[0]) হলো প্রথম বাইট এবং এর পরের মানটি (μs[1]) হলো দৈর্ঘ্য।
H.2 ইনস্ট্রাকশন সেট
EVM-এর চূড়ান্ত উপবিভাগ, 9.5-এ যাওয়ার আগে, আসুন ইনস্ট্রাকশনগুলো দেখি। এগুলো পরিশিষ্ট H.2-এ সংজ্ঞায়িত করা হয়েছে যা 30 পৃষ্ঠায় শুরু হয়। সেই নির্দিষ্ট অপকোডের সাথে পরিবর্তিত হচ্ছে বলে নির্দিষ্ট করা হয়নি এমন যেকোনো কিছু একই থাকবে বলে আশা করা হয়। যে ভেরিয়েবলগুলো পরিবর্তিত হয় সেগুলো <something>′ হিসেবে নির্দিষ্ট করা হয়।
উদাহরণস্বরূপ, আসুন ADD (নতুন ট্যাবে খোলে) অপকোডটি দেখি।
| মান | নেমোনিক | δ | α | বিবরণ |
|---|---|---|---|---|
| 0x01 | ADD | 2 | 1 | যোগের কাজ। |
| μ′s[0] ≡ μs[0] + μs[1] |
δ হলো সেই মানগুলোর সংখ্যা যা আমরা স্ট্যাক থেকে পপ করি। এই ক্ষেত্রে 2টি, কারণ আমরা শীর্ষ 2টি মান যোগ করছি।
α হলো সেই মানগুলোর সংখ্যা যা আমরা পুশ ব্যাক করি। এই ক্ষেত্রে 1টি, যোগফল।
সুতরাং নতুন স্ট্যাক শীর্ষ (μ′s[0]) হলো পুরানো স্ট্যাক শীর্ষ (μs[0]) এবং এর নীচের পুরানো মানের (μs[1]) যোগফল।
একটি "একঘেয়ে তালিকা" দিয়ে সমস্ত অপকোডগুলো দেখার পরিবর্তে, এই নিবন্ধটি কেবল সেই অপকোডগুলো ব্যাখ্যা করে যা নতুন কিছু প্রবর্তন করে।
| মান | নেমোনিক | δ | α | বিবরণ |
|---|---|---|---|---|
| 0x20 | KECCAK256 | 2 | 1 | কেক্যাক-২৫৬ হ্যাশ গণনা করুন। |
| μ′s[0] ≡ KEC(μm[μs[0] . . . (μs[0] + μs[1] − 1)]) | ||||
| μ′i ≡ M(μi,μs[0],μs[1]) |
এটি প্রথম অপকোড যা মেমরি অ্যাক্সেস করে (এই ক্ষেত্রে, রিড অনলি)। যাইহোক, এটি মেমরির বর্তমান সীমার বাইরে প্রসারিত হতে পারে, তাই আমাদের μi আপডেট করতে হবে। আমরা 30 পৃষ্ঠায় সমীকরণ 330-এ সংজ্ঞায়িত M ফাংশন ব্যবহার করে এটি করি।
| মান | নেমোনিক | δ | α | বিবরণ |
|---|---|---|---|---|
| 0x31 | BALANCE | 1 | 1 | প্রদত্ত অ্যাকাউন্টের ব্যালেন্স পান। |
| ... |
যে ঠিকানার ব্যালেন্স আমাদের খুঁজে বের করতে হবে তা হলো μs[0] mod 2160। স্ট্যাকের শীর্ষটি হলো ঠিকানা, কিন্তু যেহেতু ঠিকানাগুলো কেবল 160 বিটের, তাই আমরা 2160 মডুলো (নতুন ট্যাবে খোলে) মান গণনা করি।
যদি σ[μs[0] mod 2160] ≠ ∅ হয়, তবে এর অর্থ হলো এই ঠিকানা সম্পর্কে তথ্য রয়েছে। সেই ক্ষেত্রে, σ[μs[0] mod 2160]b হলো সেই ঠিকানার ব্যালেন্স। যদি σ[μs[0] mod 2160] = ∅ হয়, তবে এর অর্থ হলো এই ঠিকানাটি আনইনিশিয়ালাইজড এবং ব্যালেন্স 0। আপনি 4 পৃষ্ঠায় 4.1 বিভাগে অ্যাকাউন্ট তথ্য ক্ষেত্রগুলোর তালিকা দেখতে পারেন।
দ্বিতীয় সমীকরণ, A'a ≡ Aa ∪ {μs[0] mod 2160}, ওয়ার্ম স্টোরেজ (যে স্টোরেজটি সম্প্রতি অ্যাক্সেস করা হয়েছে এবং ক্যাশ হওয়ার সম্ভাবনা রয়েছে) এবং কোল্ড স্টোরেজ (যে স্টোরেজটি অ্যাক্সেস করা হয়নি এবং ধীর স্টোরেজে থাকার সম্ভাবনা রয়েছে যা পুনরুদ্ধার করা বেশি ব্যয়বহুল)-এ অ্যাক্সেসের খরচের পার্থক্যের সাথে সম্পর্কিত। Aa হলো ট্রানজ্যাকশন দ্বারা পূর্বে অ্যাক্সেস করা ঠিকানাগুলোর তালিকা, যা 9 পৃষ্ঠায় 6.1 বিভাগে সংজ্ঞায়িত করা হয়েছে, তাই অ্যাক্সেস করা সস্তা হওয়া উচিত। আপনি EIP-2929 (নতুন ট্যাবে খোলে)-এ এই বিষয়ে আরও পড়তে পারেন।
| মান | নেমোনিক | δ | α | বিবরণ |
|---|---|---|---|---|
| 0x8F | DUP16 | 16 | 17 | 16তম স্ট্যাক আইটেম ডুপ্লিকেট করুন। |
| μ′s[0] ≡ μs[15] |
মনে রাখবেন যে কোনো স্ট্যাক আইটেম ব্যবহার করতে, আমাদের এটিকে পপ করতে হবে, যার অর্থ হলো আমাদের এর উপরের সমস্ত স্ট্যাক আইটেমগুলোও পপ করতে হবে। DUP<n> (নতুন ট্যাবে খোলে) এবং SWAP<n> (নতুন ট্যাবে খোলে)-এর ক্ষেত্রে, এর অর্থ হলো 16টি পর্যন্ত মান পপ করা এবং তারপর পুশ করা।
9.5 এক্সিকিউশন সাইকেল
এখন যেহেতু আমাদের কাছে সমস্ত অংশ রয়েছে, আমরা অবশেষে বুঝতে পারি কীভাবে EVM-এর এক্সিকিউশন সাইকেল নথিভুক্ত করা হয়েছে।
সমীকরণ (164) বলে যে প্রদত্ত স্টেট:
- σ (গ্লোবাল ব্লকচেইন স্টেট)
- μ (EVM স্টেট)
- A (সাবস্টেট, ট্রানজ্যাকশন শেষ হলে যে পরিবর্তনগুলো ঘটবে)
- I (এক্সিকিউশন এনভায়রনমেন্ট)
নতুন স্টেট হলো (σ', μ', A', I')।
সমীকরণ (165)-(167) স্ট্যাক এবং একটি অপকোডের কারণে এতে পরিবর্তন (μs) সংজ্ঞায়িত করে। সমীকরণ (168) হলো গ্যাসে পরিবর্তন (μg)। সমীকরণ (169) হলো প্রোগ্রাম কাউন্টারে পরিবর্তন (μpc)। পরিশেষে, সমীকরণ (170)-(173) নির্দিষ্ট করে যে অন্যান্য প্যারামিটারগুলো একই থাকে, যদি না অপকোড দ্বারা স্পষ্টভাবে পরিবর্তন করা হয়।
এর মাধ্যমে EVM সম্পূর্ণরূপে সংজ্ঞায়িত করা হয়েছে।
উপসংহার
গাণিতিক স্বরলিপি (Mathematical notation) সুনির্দিষ্ট এবং এটি ইয়েলো পেপারকে ইথেরিয়ামের প্রতিটি বিবরণ নির্দিষ্ট করার অনুমতি দিয়েছে। যাইহোক, এর কিছু অসুবিধাও রয়েছে:
- এটি কেবল মানুষের দ্বারাই বোঝা সম্ভব, যার অর্থ হলো কমপ্লায়েন্স টেস্টগুলো (নতুন ট্যাবে খোলে) ম্যানুয়ালি লিখতে হবে।
- প্রোগ্রামাররা কম্পিউটার কোড বোঝেন। তারা গাণিতিক স্বরলিপি বুঝতেও পারেন বা নাও বুঝতে পারেন।
সম্ভবত এই কারণগুলোর জন্যই, নতুন কনসেনসাস লেয়ার স্পেসিফিকেশনগুলো (নতুন ট্যাবে খোলে) Python-এ লেখা হয়েছে। Python-এ এক্সিকিউশন লেয়ার স্পেসিফিকেশন (নতুন ট্যাবে খোলে) রয়েছে, তবে সেগুলো সম্পূর্ণ নয়। যতক্ষণ না সম্পূর্ণ ইয়েলো পেপারটিও Python বা অনুরূপ কোনো ভাষায় অনুবাদ করা হচ্ছে, ততক্ষণ ইয়েলো পেপারটি পরিষেবাতে অব্যাহত থাকবে এবং এটি পড়তে পারা সহায়ক।