Memahami Spesifikasi EVM di Yellow Paper
Yellow Paper (opens in a new tab) adalah spesifikasi formal untuk Ethereum. Kecuali jika diubah oleh proses EIP, dokumen ini berisi deskripsi yang tepat tentang bagaimana semuanya bekerja. Dokumen ini ditulis sebagai makalah matematika, yang mencakup terminologi yang mungkin tidak familier bagi pemrogram. Dalam makalah ini Anda akan belajar cara membacanya, dan lebih jauh lagi makalah matematika terkait lainnya.
Yellow Paper yang Mana?
Seperti hampir semua hal lain di Ethereum, Yellow Paper berkembang seiring waktu. Agar dapat merujuk ke versi tertentu, saya mengunggah versi saat penulisan ini. Nomor bagian, halaman, dan persamaan yang saya gunakan akan merujuk ke versi tersebut. Ada baiknya Anda membukanya di jendela yang berbeda saat membaca dokumen ini.
Mengapa EVM?
Yellow paper asli ditulis tepat pada awal pengembangan Ethereum. Dokumen ini menjelaskan mekanisme konsensus berbasis proof-of-work asli yang pada awalnya digunakan untuk mengamankan jaringan. Namun, Ethereum mematikan proof-of-work dan mulai menggunakan konsensus berbasis proof-of-stake pada bulan September 2022. Tutorial ini akan berfokus pada bagian-bagian dari yellow paper yang mendefinisikan Mesin Virtual Ethereum. EVM tidak berubah oleh transisi ke proof-of-stake (kecuali untuk nilai kembalian dari opcode DIFFICULTY).
9 Model eksekusi
Bagian ini (hal. 12-14) mencakup sebagian besar definisi EVM.
Istilah status sistem mencakup semua yang perlu Anda ketahui tentang sistem untuk menjalankannya. Pada komputer biasa, ini berarti memori, isi register, dll.
Mesin Turing (opens in a new tab) adalah model komputasi. Pada dasarnya, ini adalah versi komputer yang disederhanakan, yang terbukti memiliki kemampuan yang sama untuk menjalankan komputasi yang dapat dilakukan oleh komputer normal (semua yang dapat dihitung oleh komputer dapat dihitung oleh mesin Turing dan sebaliknya). Model ini memudahkan untuk membuktikan berbagai teorema tentang apa yang bisa dan tidak bisa dihitung.
Istilah Turing-complete (opens in a new tab) berarti komputer yang dapat menjalankan perhitungan yang sama dengan mesin Turing. Mesin Turing dapat masuk ke dalam perulangan tak terbatas, dan EVM tidak bisa karena akan kehabisan gas, jadi ini hanya quasi-Turing-complete.
9.1 Dasar-dasar
Bagian ini memberikan dasar-dasar EVM dan bagaimana perbandingannya dengan model komputasi lainnya.
Mesin stack (opens in a new tab) adalah komputer yang menyimpan data perantara bukan di register, tetapi di dalam stack (opens in a new tab). Ini adalah arsitektur yang disukai untuk mesin virtual karena mudah diimplementasikan yang berarti bug, dan kerentanan keamanan, jauh lebih kecil kemungkinannya. Memori di dalam stack dibagi menjadi kata (word) 256-bit. Ini dipilih karena nyaman untuk operasi kriptografi inti Ethereum seperti hashing Keccak-256 dan komputasi kurva eliptik. Ukuran maksimum stack adalah 1024 item (1024 x 256 bit). Saat opcode dieksekusi, mereka biasanya mendapatkan parameternya dari stack. Ada opcode khusus untuk mengatur ulang elemen di dalam stack seperti POP (menghapus item dari atas stack), DUP_N (menduplikasi item ke-N di dalam stack), dll.
EVM juga memiliki ruang volatil yang disebut memori yang digunakan untuk menyimpan data selama eksekusi. Memori ini diatur ke dalam kata 32-byte. Semua lokasi memori diinisialisasi ke nol. Jika Anda mengeksekusi kode Yul (opens in a new tab) ini untuk menambahkan sebuah kata ke memori, itu akan mengisi 32 byte memori dengan menambahkan nol pada ruang kosong di dalam kata tersebut, yaitu, ia membuat satu kata - dengan nol di lokasi 0-29, 0x60 ke 30, dan 0xA7 ke 31.
1mstore(0, 0x60A7)mstore adalah satu dari tiga opcode yang disediakan EVM untuk berinteraksi dengan memori - ia memuat sebuah kata ke dalam memori. Dua lainnya adalah mstore8 yang memuat satu byte ke dalam memori, dan mload yang memindahkan sebuah kata dari memori ke stack.
EVM juga memiliki model penyimpanan non-volatil terpisah yang dipertahankan sebagai bagian dari status sistem - memori ini diatur ke dalam array kata (berlawanan dengan array byte yang dapat dialamatkan kata di dalam stack). Penyimpanan ini adalah tempat kontrak menyimpan data persisten - sebuah kontrak hanya dapat berinteraksi dengan penyimpanannya sendiri. Penyimpanan diatur dalam pemetaan nilai-kunci (key-value).
Meskipun tidak disebutkan di bagian Yellow Paper ini, ada baiknya juga untuk mengetahui bahwa ada jenis memori keempat. Calldata adalah memori baca-saja yang dapat dialamatkan byte yang digunakan untuk menyimpan nilai yang diteruskan dengan parameter data dari sebuah transaksi. EVM memiliki opcode khusus untuk mengelola calldata. calldatasize mengembalikan ukuran data. calldataload memuat data ke dalam stack. calldatacopy menyalin data ke dalam memori.
Arsitektur Von Neumann (opens in a new tab) standar menyimpan kode dan data di memori yang sama. EVM tidak mengikuti standar ini karena alasan keamanan - berbagi memori volatil memungkinkan untuk mengubah kode program. Sebaliknya, kode disimpan ke penyimpanan.
Hanya ada dua kasus di mana kode dieksekusi dari memori:
- Saat sebuah kontrak membuat kontrak lain (menggunakan
CREATE(opens in a new tab) atauCREATE2(opens in a new tab)), kode untuk konstruktor kontrak berasal dari memori. - Selama pembuatan kontrak apa pun, kode konstruktor berjalan dan kemudian kembali dengan kode kontrak yang sebenarnya, juga dari memori.
Istilah eksekusi luar biasa (exceptional execution) berarti pengecualian yang menyebabkan eksekusi kontrak saat ini berhenti.
9.2 Gambaran umum biaya
Bagian ini menjelaskan bagaimana biaya gas dihitung. Ada tiga biaya:
Biaya opcode
Biaya bawaan dari opcode tertentu. Untuk mendapatkan nilai ini, temukan grup biaya opcode di Lampiran H (hal. 28, di bawah persamaan (327)), dan temukan grup biaya di persamaan (324). Ini memberi Anda fungsi biaya, yang dalam banyak kasus menggunakan parameter dari Lampiran G (hal. 27).
Misalnya, opcode CALLDATACOPY (opens in a new tab) adalah anggota grup Wcopy. Biaya opcode untuk grup tersebut adalah Gverylow+Gcopy×⌈μs[2]÷32⌉. Melihat Lampiran G, kita melihat bahwa kedua konstanta adalah 3, yang memberi kita 3+3×⌈μs[2]÷32⌉.
Kita masih perlu menguraikan ekspresi ⌈μs[2]÷32⌉. Bagian terluar, ⌈ <value> ⌉ adalah fungsi ceiling (pembulatan ke atas), sebuah fungsi yang jika diberikan sebuah nilai akan mengembalikan bilangan bulat terkecil yang masih tidak lebih kecil dari nilai tersebut. Misalnya, ⌈2.5⌉ = ⌈3⌉ = 3. Bagian dalamnya adalah μs[2]÷32. Melihat bagian 3 (Konvensi) di hal. 3, μ adalah status mesin. Status mesin didefinisikan di bagian 9.4.1 di hal. 13. Menurut bagian tersebut, salah satu parameter status mesin adalah s untuk stack. Menggabungkan semuanya, tampaknya μs[2] adalah lokasi #2 di dalam stack. Melihat opcode tersebut (opens in a new tab), lokasi #2 di dalam stack adalah ukuran data dalam byte. Melihat opcode lain di grup Wcopy, CODECOPY (opens in a new tab) dan RETURNDATACOPY (opens in a new tab), mereka juga memiliki ukuran data di lokasi yang sama. Jadi ⌈μs[2]÷32⌉ adalah jumlah kata 32 byte yang diperlukan untuk menyimpan data yang sedang disalin. Menggabungkan semuanya, biaya bawaan dari CALLDATACOPY (opens in a new tab) adalah 3 gas ditambah 3 per kata dari data yang sedang disalin.
Biaya berjalan
Biaya menjalankan kode yang kita panggil.
- Dalam kasus
CREATE(opens in a new tab) danCREATE2(opens in a new tab), konstruktor untuk kontrak baru. - Dalam kasus
CALL(opens in a new tab),CALLCODE(opens in a new tab),STATICCALL(opens in a new tab), atauDELEGATECALL(opens in a new tab), kontrak yang kita panggil.
Biaya perluasan memori
Biaya memperluas memori (jika perlu).
Dalam persamaan 324, nilai ini ditulis sebagai Cmem(μi')-Cmem(μi). Melihat bagian 9.4.1 lagi, kita melihat bahwa μi adalah jumlah kata di dalam memori. Jadi μi adalah jumlah kata di dalam memori sebelum opcode dan μi' adalah jumlah kata di dalam memori setelah opcode.
Fungsi Cmem didefinisikan dalam persamaan 326: Cmem(a) = Gmemory × a + ⌊a2 ÷ 512⌋. ⌊x⌋ adalah fungsi floor (pembulatan ke bawah), sebuah fungsi yang jika diberikan sebuah nilai akan mengembalikan bilangan bulat terbesar yang masih tidak lebih besar dari nilai tersebut. Misalnya, ⌊2.5⌋ = ⌊2⌋ = 2. Ketika a < √512, a2 < 512, dan hasil dari fungsi floor adalah nol. Jadi untuk 22 kata pertama (704 byte), biayanya naik secara linier dengan jumlah kata memori yang diperlukan. Di luar titik itu ⌊a2 ÷ 512⌋ bernilai positif. Ketika memori yang diperlukan cukup tinggi, biaya gas sebanding dengan kuadrat dari jumlah memori.
Catatan bahwa faktor-faktor ini hanya memengaruhi biaya gas bawaan - ini tidak memperhitungkan pasar biaya atau tip kepada validator yang menentukan berapa banyak pengguna akhir diharuskan membayar - ini hanyalah biaya mentah untuk menjalankan operasi tertentu di EVM.
Baca lebih lanjut tentang gas.
9.3 Lingkungan eksekusi
Lingkungan eksekusi adalah sebuah tuple, I, yang mencakup informasi yang bukan bagian dari status blockchain atau EVM.
| Parameter | Opcode untuk mengakses data | Kode Solidity untuk mengakses data |
|---|---|---|
| Ia | ADDRESS (opens in a new tab) | address(this) |
| Io | ORIGIN (opens in a new tab) | tx.origin |
| Ip | GASPRICE (opens in a new tab) | tx.gasprice |
| Id | CALLDATALOAD (opens in a new tab), dll. | msg.data |
| Is | CALLER (opens in a new tab) | msg.sender |
| Iv | CALLVALUE (opens in a new tab) | msg.value |
| Ib | CODECOPY (opens in a new tab) | address(this).code |
| IH | Bidang header blok, seperti NUMBER (opens in a new tab) dan DIFFICULTY (opens in a new tab) | block.number, block.difficulty, dll. |
| Ie | Kedalaman call stack untuk panggilan antar kontrak (termasuk pembuatan kontrak) | |
| Iw | Apakah EVM diizinkan untuk mengubah status, atau apakah ia berjalan secara statis |
Beberapa parameter lain diperlukan untuk memahami sisa bagian 9:
| Parameter | Didefinisikan di bagian | Arti |
|---|---|---|
| σ | 2 (hal. 2, persamaan 1) | Status blockchain |
| g | 9.3 (hal. 13) | Sisa gas |
| A | 6.1 (hal. 8) | Substatus yang masih harus dibayar (perubahan yang dijadwalkan saat transaksi berakhir) |
| o | 9.3 (hal. 13) | Output - hasil yang dikembalikan dalam kasus transaksi internal (saat satu kontrak memanggil kontrak lain) dan panggilan ke fungsi view (saat Anda hanya meminta informasi, jadi tidak perlu menunggu transaksi) |
9.4 Gambaran umum eksekusi
Sekarang setelah memiliki semua pendahuluan, kita akhirnya dapat mulai mengerjakan bagaimana EVM bekerja.
Persamaan 137-142 memberi kita kondisi awal untuk menjalankan EVM:
| Simbol | Nilai awal | Arti |
|---|---|---|
| μg | g | Sisa gas |
| μpc | 0 | Program counter, alamat instruksi berikutnya yang akan dieksekusi |
| μm | (0, 0, ...) | Memori, diinisialisasi ke semua nol |
| μi | 0 | Lokasi memori tertinggi yang digunakan |
| μs | () | Stack, awalnya kosong |
| μo | ∅ | Output, himpunan kosong sampai dan kecuali kita berhenti baik dengan data kembalian (RETURN (opens in a new tab) atau REVERT (opens in a new tab)) atau tanpanya (STOP (opens in a new tab) atau SELFDESTRUCT (opens in a new tab)). |
Persamaan 143 memberi tahu kita bahwa ada empat kemungkinan kondisi pada setiap titik waktu selama eksekusi, dan apa yang harus dilakukan dengannya:
Z(σ,μ,A,I). Z mewakili fungsi yang menguji apakah suatu operasi menciptakan transisi status yang tidak valid (lihat penghentian luar biasa). Jika dievaluasi menjadi True, status baru identik dengan yang lama (kecuali gas terbakar) karena perubahan belum diimplementasikan.- Jika opcode yang dieksekusi adalah
REVERT(opens in a new tab), status baru sama dengan status lama, sejumlah gas hilang. - Jika urutan operasi selesai, seperti yang ditandai oleh
RETURN(opens in a new tab)), status diperbarui ke status baru. - Jika kita tidak berada di salah satu kondisi akhir 1-3, lanjutkan berjalan.
9.4.1 Status Mesin
Bagian ini menjelaskan status mesin secara lebih rinci. Ini menentukan bahwa w adalah opcode saat ini. Jika μpc kurang dari ||Ib||, panjang kode, maka byte tersebut (Ib[μpc]) adalah opcode. Jika tidak, opcode didefinisikan sebagai STOP (opens in a new tab).
Karena ini adalah mesin stack (opens in a new tab), kita perlu melacak jumlah item yang dikeluarkan (δ) dan dimasukkan (α) oleh setiap opcode.
9.4.2 Penghentian Luar Biasa
Bagian ini mendefinisikan fungsi Z, yang menentukan kapan kita memiliki penghentian abnormal. Ini adalah fungsi Boolean (opens in a new tab), jadi ia menggunakan ∨ untuk logika or (opens in a new tab) dan ∧ untuk logika and (opens in a new tab).
Kita memiliki penghentian luar biasa jika salah satu dari kondisi ini benar:
-
μg < C(σ,μ,A,I) Seperti yang kita lihat di bagian 9.2, C adalah fungsi yang menentukan biaya gas. Tidak ada cukup gas yang tersisa untuk menutupi opcode berikutnya.
-
δw=∅ Jika jumlah item yang dikeluarkan untuk sebuah opcode tidak terdefinisi, maka opcode itu sendiri tidak terdefinisi.
-
|| μs || < δw Stack underflow, tidak cukup item di dalam stack untuk opcode saat ini.
-
w = JUMP ∧ μs[0]∉D(Ib) Opcode-nya adalah
JUMP(opens in a new tab) dan alamatnya bukanJUMPDEST(opens in a new tab). Lompatan hanya valid ketika tujuannya adalahJUMPDEST(opens in a new tab). -
w = JUMPI ∧ μs[1]≠0 ∧ μs[0] ∉ D(Ib) Opcode-nya adalah
JUMPI(opens in a new tab), kondisinya benar (bukan nol) sehingga lompatan harus terjadi, dan alamatnya bukanJUMPDEST(opens in a new tab). Lompatan hanya valid ketika tujuannya adalahJUMPDEST(opens in a new tab). -
w = RETURNDATACOPY ∧ μs[1]+μs[2]>|| μo || Opcode-nya adalah
RETURNDATACOPY(opens in a new tab). Dalam opcode ini elemen stack μs[1] adalah offset untuk membaca dari buffer data kembalian, dan elemen stack μs[2] adalah panjang data. Kondisi ini terjadi ketika Anda mencoba membaca melampaui akhir buffer data kembalian. Perhatikan bahwa tidak ada kondisi serupa untuk calldata atau untuk kode itu sendiri. Ketika Anda mencoba membaca melampaui akhir buffer tersebut, Anda hanya mendapatkan nol. -
|| μs || - δw + αw > 1024
Stack overflow. Jika menjalankan opcode akan menghasilkan stack lebih dari 1024 item, batalkan.
-
¬Iw ∧ W(w,μ) Apakah kita berjalan secara statis (¬ adalah negasi (opens in a new tab) dan Iw bernilai benar ketika kita diizinkan untuk mengubah status blockchain)? Jika ya, dan kita mencoba operasi pengubahan status, itu tidak bisa terjadi.
Fungsi W(w,μ) didefinisikan kemudian dalam persamaan 150. W(w,μ) bernilai benar jika salah satu dari kondisi ini benar:
-
w ∈ {CREATE, CREATE2, SSTORE, SELFDESTRUCT} Opcode ini mengubah status, baik dengan membuat kontrak baru, menyimpan nilai, atau menghancurkan kontrak saat ini.
-
LOG0≤w ∧ w≤LOG4 Jika kita dipanggil secara statis, kita tidak dapat memancarkan entri log. Semua opcode log berada dalam kisaran antara
LOG0(A0) (opens in a new tab) danLOG4(A4) (opens in a new tab). Angka setelah opcode log menentukan berapa banyak topik yang dikandung entri log. -
w=CALL ∧ μs[2]≠0 Anda dapat memanggil kontrak lain saat Anda statis, tetapi jika Anda melakukannya, Anda tidak dapat mentransfer ETH ke dalamnya.
-
-
w = SSTORE ∧ μg ≤ Gcallstipend Anda tidak dapat menjalankan
SSTORE(opens in a new tab) kecuali Anda memiliki lebih dari Gcallstipend (didefinisikan sebagai 2300 di Lampiran G) gas.
9.4.3 Validitas Tujuan Lompatan
Di sini kita secara formal mendefinisikan apa itu opcode JUMPDEST (opens in a new tab). Kita tidak bisa hanya mencari nilai byte 0x5B, karena itu mungkin berada di dalam PUSH (dan karenanya merupakan data dan bukan opcode).
Dalam persamaan (153) kita mendefinisikan sebuah fungsi, N(i,w). Parameter pertama, i, adalah lokasi opcode. Yang kedua, w, adalah opcode itu sendiri. Jika w∈[PUSH1, PUSH32] itu berarti opcode-nya adalah PUSH (tanda kurung siku mendefinisikan rentang yang mencakup titik akhir). Jika demikian, opcode berikutnya berada di i+2+(w−PUSH1). Untuk PUSH1 (opens in a new tab) kita perlu maju dua byte (PUSH itu sendiri dan nilai satu byte), untuk PUSH2 (opens in a new tab) kita perlu maju tiga byte karena itu adalah nilai dua byte, dll. Semua opcode EVM lainnya hanya sepanjang satu byte, jadi dalam semua kasus lain N(i,w)=i+1.
Fungsi ini digunakan dalam persamaan (152) untuk mendefinisikan DJ(c,i), yang merupakan himpunan (opens in a new tab) dari semua tujuan lompatan yang valid dalam kode c, dimulai dengan lokasi opcode i. Fungsi ini didefinisikan secara rekursif. Jika i≥||c||, itu berarti kita berada di atau setelah akhir kode. Kita tidak akan menemukan tujuan lompatan lagi, jadi cukup kembalikan himpunan kosong.
Dalam semua kasus lain, kita melihat sisa kode dengan pergi ke opcode berikutnya dan mendapatkan himpunan yang dimulai darinya. c[i] adalah opcode saat ini, jadi N(i,c[i]) adalah lokasi opcode berikutnya. Oleh karena itu, DJ(c,N(i,c[i])) adalah himpunan tujuan lompatan yang valid yang dimulai pada opcode berikutnya. Jika opcode saat ini bukan JUMPDEST, cukup kembalikan himpunan tersebut. Jika itu adalah JUMPDEST, sertakan dalam himpunan hasil dan kembalikan itu.
9.4.4 Penghentian normal
Fungsi penghentian H, dapat mengembalikan tiga jenis nilai.
- Jika kita tidak berada dalam opcode penghentian, kembalikan ∅, himpunan kosong. Berdasarkan konvensi, nilai ini ditafsirkan sebagai Boolean salah (false).
- Jika kita memiliki opcode penghentian yang tidak menghasilkan output (baik
STOP(opens in a new tab) atauSELFDESTRUCT(opens in a new tab)), kembalikan urutan byte berukuran nol sebagai nilai kembalian. Perhatikan bahwa ini sangat berbeda dari himpunan kosong. Nilai ini berarti bahwa EVM benar-benar berhenti, hanya saja tidak ada data kembalian untuk dibaca. - Jika kita memiliki opcode penghentian yang menghasilkan output (baik
RETURN(opens in a new tab) atauREVERT(opens in a new tab)), kembalikan urutan byte yang ditentukan oleh opcode tersebut. Urutan ini diambil dari memori, nilai di bagian atas stack (μs[0]) adalah byte pertama, dan nilai setelahnya (μs[1]) adalah panjangnya.
H.2 Set instruksi
Sebelum kita pergi ke subbagian terakhir dari EVM, 9.5, mari kita lihat instruksinya sendiri. Mereka didefinisikan dalam Lampiran H.2 yang dimulai pada hal. 29. Apa pun yang tidak ditentukan sebagai berubah dengan opcode tertentu diharapkan tetap sama. Variabel yang berubah ditentukan dengan sebagai <sesuatu>′.
Misalnya, mari kita lihat opcode ADD (opens in a new tab).
| Nilai | Mnemonic | δ | α | Deskripsi |
|---|---|---|---|---|
| 0x01 | ADD | 2 | 1 | Operasi penambahan. |
| μ′s[0] ≡ μs[0] + μs[1] |
δ adalah jumlah nilai yang kita keluarkan dari stack. Dalam hal ini dua, karena kita menambahkan dua nilai teratas.
α adalah jumlah nilai yang kita masukkan kembali. Dalam hal ini satu, jumlahnya.
Jadi bagian atas stack yang baru (μ′s[0]) adalah jumlah dari bagian atas stack yang lama (μs[0]) dan nilai lama di bawahnya (μs[1]).
Daripada membahas semua opcode dengan "daftar yang membosankan", artikel ini hanya menjelaskan opcode yang memperkenalkan sesuatu yang baru.
| Nilai | Mnemonic | δ | α | Deskripsi |
|---|---|---|---|---|
| 0x20 | KECCAK256 | 2 | 1 | Hitung hash Keccak-256. |
| μ′s[0] ≡ KEC(μm[μs[0] . . . (μs[0] + μs[1] − 1)]) | ||||
| μ′i ≡ M(μi,μs[0],μs[1]) |
Ini adalah opcode pertama yang mengakses memori (dalam hal ini, baca saja). Namun, ini mungkin meluas melampaui batas memori saat ini, jadi kita perlu memperbarui μi. Kita melakukan ini menggunakan fungsi M yang didefinisikan dalam persamaan 328 di hal. 29.
| Nilai | Mnemonic | δ | α | Deskripsi |
|---|---|---|---|---|
| 0x31 | BALANCE | 1 | 1 | Dapatkan saldo dari akun yang diberikan. |
| ... |
Alamat yang saldonya perlu kita temukan adalah μs[0] mod 2160. Bagian atas stack adalah alamatnya, tetapi karena alamat hanya 160 bit, kita menghitung nilai modulo (opens in a new tab) 2160.
Jika σ[μs[0] mod 2160] ≠ ∅, itu berarti ada informasi tentang alamat ini. Dalam hal ini, σ[μs[0] mod 2160]b adalah saldo untuk alamat tersebut. Jika σ[μs[0] mod 2160] = ∅, itu berarti alamat ini tidak diinisialisasi dan saldonya nol. Anda dapat melihat daftar bidang informasi akun di bagian 4.1 di hal. 4.
Persamaan kedua, A'a ≡ Aa ∪ {μs[0] mod 2160}, terkait dengan perbedaan biaya antara akses ke penyimpanan hangat (penyimpanan yang baru-baru ini diakses dan kemungkinan di-cache) dan penyimpanan dingin (penyimpanan yang belum diakses dan kemungkinan berada di penyimpanan yang lebih lambat yang lebih mahal untuk diambil). Aa adalah daftar alamat yang sebelumnya diakses oleh transaksi, yang karenanya harus lebih murah untuk diakses, seperti yang didefinisikan di bagian 6.1 di hal. 8. Anda dapat membaca lebih lanjut tentang subjek ini di EIP-2929 (opens in a new tab).
| Nilai | Mnemonic | δ | α | Deskripsi |
|---|---|---|---|---|
| 0x8F | DUP16 | 16 | 17 | Duplikasi item stack ke-16. |
| μ′s[0] ≡ μs[15] |
Perhatikan bahwa untuk menggunakan item stack apa pun, kita perlu mengeluarkannya, yang berarti kita juga perlu mengeluarkan semua item stack di atasnya. Dalam kasus DUP<n> (opens in a new tab) dan SWAP<n> (opens in a new tab), ini berarti harus mengeluarkan dan kemudian memasukkan hingga enam belas nilai.
9.5 Siklus eksekusi
Sekarang setelah kita memiliki semua bagiannya, kita akhirnya dapat memahami bagaimana siklus eksekusi EVM didokumentasikan.
Persamaan (155) mengatakan bahwa dengan status:
- σ (status blockchain global)
- μ (status EVM)
- A (substatus, perubahan yang terjadi saat transaksi berakhir)
- I (lingkungan eksekusi)
Status baru adalah (σ', μ', A', I').
Persamaan (156)-(158) mendefinisikan stack dan perubahannya karena sebuah opcode (μs). Persamaan (159) adalah perubahan gas (μg). Persamaan (160) adalah perubahan dalam program counter (μpc). Terakhir, persamaan (161)-(164) menentukan bahwa parameter lain tetap sama, kecuali diubah secara eksplisit oleh opcode.
Dengan ini EVM sepenuhnya terdefinisi.
Kesimpulan
Notasi matematika sangat tepat dan telah memungkinkan Yellow Paper untuk menentukan setiap detail Ethereum. Namun, ini memiliki beberapa kelemahan:
- Ini hanya dapat dipahami oleh manusia, yang berarti bahwa tes kepatuhan (opens in a new tab) harus ditulis secara manual.
- Pemrogram memahami kode komputer. Mereka mungkin atau mungkin tidak memahami notasi matematika.
Mungkin karena alasan ini, spesifikasi lapisan konsensus (opens in a new tab) yang lebih baru ditulis dengan Python. Ada spesifikasi lapisan eksekusi dengan Python (opens in a new tab), tetapi belum lengkap. Sampai dan kecuali seluruh Yellow Paper juga diterjemahkan ke Python atau bahasa serupa, Yellow Paper akan terus digunakan, dan sangat membantu untuk dapat membacanya.
Pembaruan terakhir halaman: 1 Februari 2026