Chuyển đến nội dung chính

Hiểu về các thông số kỹ thuật EVM trong Sách vàng

evm
Trung cấp
qbzzt
15 tháng 5, 2022
24 phút đọc

Sách vàng (opens in a new tab) là tài liệu đặc tả chính thức của Ethereum. Ngoại trừ những phần được sửa đổi bởi quy trình EIP, nó chứa mô tả chính xác về cách mọi thứ hoạt động. Nó được viết dưới dạng một bài báo toán học, bao gồm các thuật ngữ mà các lập trình viên có thể không quen thuộc. Trong bài viết này, bạn sẽ học cách đọc nó, và rộng hơn là các bài báo toán học liên quan khác.

Phiên bản Sách vàng nào?

Giống như hầu hết mọi thứ khác trong Ethereum, Sách vàng phát triển theo thời gian. Để có thể tham khảo một phiên bản cụ thể, tôi đã tải lên phiên bản hiện tại tại thời điểm viết bài. Các số phần, trang và phương trình mà tôi sử dụng sẽ tham chiếu đến phiên bản đó. Bạn nên mở nó trong một cửa sổ khác khi đọc tài liệu này.

Tại sao lại là EVM?

Sách vàng ban đầu được viết ngay từ khi bắt đầu phát triển Ethereum. Nó mô tả cơ chế đồng thuận Bằng chứng công việc (PoW) ban đầu được sử dụng để bảo mật mạng lưới. Tuy nhiên, Ethereum đã tắt Bằng chứng công việc (PoW) và bắt đầu sử dụng cơ chế đồng thuận Bằng chứng cổ phần (PoS) vào tháng 9 năm 2022. Hướng dẫn này sẽ tập trung vào các phần của sách vàng định nghĩa Máy ảo Ethereum (EVM). EVM không bị thay đổi bởi quá trình chuyển đổi sang Bằng chứng cổ phần (PoS) (ngoại trừ giá trị trả về của mã lệnh DIFFICULTY).

9 Mô hình thực thi

Phần này (trang 12-14) bao gồm hầu hết định nghĩa về EVM.

Thuật ngữ trạng thái hệ thống bao gồm mọi thứ bạn cần biết về hệ thống để chạy nó. Trong một máy tính thông thường, điều này có nghĩa là bộ nhớ, nội dung của các thanh ghi, v.v.

Một máy Turing (opens in a new tab) là một mô hình tính toán. Về cơ bản, nó là một phiên bản đơn giản hóa của máy tính, được chứng minh là có cùng khả năng chạy các phép tính mà một máy tính bình thường có thể làm (mọi thứ mà máy tính có thể tính toán thì máy Turing cũng có thể tính toán và ngược lại). Mô hình này giúp dễ dàng chứng minh các định lý khác nhau về những gì có thể và không thể tính toán được.

Thuật ngữ Turing-complete (opens in a new tab) (hoàn thiện Turing) có nghĩa là một máy tính có thể chạy các phép tính giống như một máy Turing. Máy Turing có thể rơi vào các vòng lặp vô hạn, còn EVM thì không thể vì nó sẽ cạn kiệt Gas, do đó nó chỉ là quasi-Turing-complete (gần hoàn thiện Turing).

9.1 Kiến thức cơ bản

Phần này cung cấp những kiến thức cơ bản về EVM và cách nó so sánh với các mô hình tính toán khác.

Một máy ngăn xếp (stack machine) (opens in a new tab) là một máy tính lưu trữ dữ liệu trung gian không phải trong các thanh ghi, mà trong một ngăn xếp (stack) (opens in a new tab). Đây là kiến trúc được ưa chuộng cho các máy ảo vì nó dễ triển khai, đồng nghĩa với việc các lỗi và lỗ hổng bảo mật ít có khả năng xảy ra hơn nhiều. Bộ nhớ trong ngăn xếp được chia thành các từ (word) 256-bit. Điều này được chọn vì nó thuận tiện cho các hoạt động mật mã cốt lõi của Ethereum như Quá trình băm Keccak-256 và các phép tính đường cong elliptic. Kích thước tối đa của ngăn xếp là 1024 mục (1024 x 256 bit). Khi các mã lệnh được thực thi, chúng thường lấy các tham số từ ngăn xếp. Có các mã lệnh dành riêng cho việc tổ chức lại các phần tử trong ngăn xếp như POP (xóa mục khỏi đỉnh ngăn xếp), DUP_N (nhân bản mục thứ N trong ngăn xếp), v.v.

EVM cũng có một không gian dễ bay hơi gọi là bộ nhớ (memory) được sử dụng để lưu trữ dữ liệu trong quá trình thực thi. Bộ nhớ này được tổ chức thành các từ 32-byte. Tất cả các vị trí bộ nhớ đều được khởi tạo bằng 0. Nếu bạn thực thi mã Yul (opens in a new tab) này để thêm một từ vào bộ nhớ, nó sẽ lấp đầy 32 byte bộ nhớ bằng cách đệm không gian trống trong từ bằng các số 0, tức là nó tạo ra một từ - với các số 0 ở vị trí 0-29, 0x60 ở vị trí 30 và 0xA7 ở vị trí 31.

mstore(0, 0x60A7)

mstore là một trong ba mã lệnh mà EVM cung cấp để tương tác với bộ nhớ - nó tải một từ vào bộ nhớ. Hai mã lệnh còn lại là mstore8 tải một byte duy nhất vào bộ nhớ và mload di chuyển một từ từ bộ nhớ sang ngăn xếp.

EVM cũng có một mô hình lưu trữ (storage) không bay hơi riêng biệt được duy trì như một phần của trạng thái hệ thống - bộ nhớ này được tổ chức thành các mảng từ (trái ngược với các mảng byte có thể định địa chỉ theo từ trong ngăn xếp). Lưu trữ này là nơi các hợp đồng giữ dữ liệu liên tục - một hợp đồng chỉ có thể tương tác với bộ lưu trữ của chính nó. Lưu trữ được tổ chức theo các ánh xạ khóa-giá trị.

Mặc dù không được đề cập trong phần này của Sách vàng, nhưng cũng hữu ích khi biết có một loại bộ nhớ thứ tư. Calldata (dữ liệu lệnh gọi) là bộ nhớ chỉ đọc có thể định địa chỉ theo byte được sử dụng để lưu trữ giá trị được truyền cùng với tham số data của một giao dịch. EVM có các mã lệnh cụ thể để quản lý calldata. calldatasize trả về kích thước của dữ liệu. calldataload tải dữ liệu vào ngăn xếp. calldatacopy sao chép dữ liệu vào bộ nhớ.

Kiến trúc Von Neumann (opens in a new tab) tiêu chuẩn lưu trữ mã và dữ liệu trong cùng một bộ nhớ. EVM không tuân theo tiêu chuẩn này vì lý do bảo mật - việc chia sẻ bộ nhớ dễ bay hơi khiến cho việc thay đổi mã chương trình trở nên khả thi. Thay vào đó, mã được lưu vào bộ lưu trữ.

Chỉ có hai trường hợp mã được thực thi từ bộ nhớ:

  • Khi một hợp đồng tạo ra một hợp đồng khác (sử dụng CREATE (opens in a new tab) hoặc CREATE2 (opens in a new tab)), mã cho hàm khởi tạo hợp đồng đến từ bộ nhớ.
  • Trong quá trình tạo bất kỳ hợp đồng nào, mã hàm khởi tạo sẽ chạy và sau đó trả về mã của hợp đồng thực tế, cũng từ bộ nhớ.

Thuật ngữ thực thi ngoại lệ (exceptional execution) có nghĩa là một ngoại lệ khiến việc thực thi hợp đồng hiện tại bị dừng lại.

9.2 Tổng quan về phí

Phần này giải thích cách tính phí Gas. Có ba loại chi phí:

Chi phí mã lệnh

Chi phí cố hữu của mã lệnh cụ thể. Để lấy giá trị này, hãy tìm nhóm chi phí của mã lệnh trong Phụ lục H (trang 28, dưới phương trình (327)) và tìm nhóm chi phí trong phương trình (324). Điều này cung cấp cho bạn một hàm chi phí, trong hầu hết các trường hợp sử dụng các tham số từ Phụ lục G (trang 27).

Ví dụ, mã lệnh CALLDATACOPY (opens in a new tab) là một thành viên của nhóm Wcopy. Chi phí mã lệnh cho nhóm đó là Gverylow+Gcopy×⌈μs[2]÷32⌉. Nhìn vào Phụ lục G, chúng ta thấy rằng cả hai hằng số đều là 3, điều này cho chúng ta 3+3×⌈μs[2]÷32⌉.

Chúng ta vẫn cần giải mã biểu thức ⌈μs[2]÷32⌉. Phần ngoài cùng, ⌈ <value> ⌉ là hàm trần (ceiling function), một hàm mà khi nhận một giá trị sẽ trả về số nguyên nhỏ nhất không nhỏ hơn giá trị đó. Ví dụ, ⌈2.5⌉ = ⌈3⌉ = 3. Phần bên trong là μs[2]÷32. Nhìn vào phần 3 (Quy ước) trên trang 3, μ là trạng thái máy. Trạng thái máy được định nghĩa trong phần 9.4.1 trên trang 13. Theo phần đó, một trong các tham số trạng thái máy là s cho ngăn xếp. Kết hợp tất cả lại, có vẻ như μs[2] là vị trí số 2 trong ngăn xếp. Nhìn vào mã lệnh (opens in a new tab), vị trí số 2 trong ngăn xếp là kích thước của dữ liệu tính bằng byte. Nhìn vào các mã lệnh khác trong nhóm Wcopy, CODECOPY (opens in a new tab)RETURNDATACOPY (opens in a new tab), chúng cũng có kích thước dữ liệu ở cùng một vị trí. Vì vậy, ⌈μs[2]÷32⌉ là số lượng từ 32 byte cần thiết để lưu trữ dữ liệu đang được sao chép. Kết hợp mọi thứ lại, chi phí cố hữu của CALLDATACOPY (opens in a new tab) là 3 Gas cộng với 3 Gas cho mỗi từ dữ liệu được sao chép.

Chi phí chạy

Chi phí chạy mã mà chúng ta đang gọi.

Chi phí mở rộng bộ nhớ

Chi phí mở rộng bộ nhớ (nếu cần thiết).

Trong phương trình 324, giá trị này được viết là Cmemi')-Cmemi). Nhìn lại phần 9.4.1, chúng ta thấy rằng μi là số lượng từ trong bộ nhớ. Vì vậy, μi là số lượng từ trong bộ nhớ trước mã lệnh và μi' là số lượng từ trong bộ nhớ sau mã lệnh.

Hàm Cmem được định nghĩa trong phương trình 326: Cmem(a) = Gmemory × a + ⌊a2 ÷ 512⌋. ⌊x⌋ là hàm sàn (floor function), một hàm mà khi nhận một giá trị sẽ trả về số nguyên lớn nhất không lớn hơn giá trị đó. Ví dụ, ⌊2.5⌋ = ⌊2⌋ = 2. Khi a < √512, a2 < 512, và kết quả của hàm sàn là 0. Vì vậy, đối với 22 từ đầu tiên (704 byte), chi phí tăng tuyến tính với số lượng từ bộ nhớ được yêu cầu. Vượt qua điểm đó, ⌊a2 ÷ 512⌋ là số dương. Khi bộ nhớ yêu cầu đủ cao, chi phí Gas tỷ lệ thuận với bình phương lượng bộ nhớ.

Lưu ý rằng các yếu tố này chỉ ảnh hưởng đến chi phí Gas cố hữu - nó không tính đến thị trường phí hoặc tiền boa cho các trình xác thực quyết định số tiền mà người dùng cuối phải trả - đây chỉ là chi phí thô để chạy một hoạt động cụ thể trên EVM.

Đọc thêm về Gas.

9.3 Môi trường thực thi

Môi trường thực thi là một bộ (tuple), I, bao gồm thông tin không thuộc về trạng thái Chuỗi khối hoặc EVM.

Tham sốMã lệnh để truy cập dữ liệuMã Solidity để truy cập dữ liệu
IaADDRESS (opens in a new tab)address(this)
IoORIGIN (opens in a new tab)tx.origin
IpGASPRICE (opens in a new tab)tx.gasprice
IdCALLDATALOAD (opens in a new tab), v.v.msg.data
IsCALLER (opens in a new tab)msg.sender
IvCALLVALUE (opens in a new tab)msg.value
IbCODECOPY (opens in a new tab)address(this).code
IHCác trường tiêu đề block, chẳng hạn như NUMBER (opens in a new tab)DIFFICULTY (opens in a new tab)block.number, block.difficulty, v.v.
IeĐộ sâu của ngăn xếp lệnh gọi cho các lệnh gọi giữa các hợp đồng (bao gồm cả việc tạo hợp đồng)
IwEVM có được phép thay đổi trạng thái hay không, hay nó đang chạy tĩnh

Một vài tham số khác cần thiết để hiểu phần còn lại của phần 9:

Tham sốĐược định nghĩa trong phầnÝ nghĩa
σ2 (trang 2, phương trình 1)Trạng thái của Chuỗi khối
g9.3 (trang 13)Gas còn lại
A6.1 (trang 8)Trạng thái phụ tích lũy (các thay đổi được lên lịch khi giao dịch kết thúc)
o9.3 (trang 13)Đầu ra - kết quả trả về trong trường hợp giao dịch nội bộ (khi một hợp đồng gọi một hợp đồng khác) và các lệnh gọi đến các hàm view (khi bạn chỉ yêu cầu thông tin, vì vậy không cần phải đợi một giao dịch)

9.4 Tổng quan về thực thi

Bây giờ đã có tất cả các bước chuẩn bị, cuối cùng chúng ta có thể bắt đầu tìm hiểu cách EVM hoạt động.

Các phương trình 137-142 cung cấp cho chúng ta các điều kiện ban đầu để chạy EVM:

Ký hiệuGiá trị ban đầuÝ nghĩa
μggGas còn lại
μpc0Bộ đếm chương trình, Địa chỉ của lệnh tiếp theo sẽ thực thi
μm(0, 0, ...)Bộ nhớ, được khởi tạo toàn bộ bằng 0
μi0Vị trí bộ nhớ cao nhất được sử dụng
μs()Ngăn xếp, ban đầu trống
μoĐầu ra, tập hợp rỗng cho đến khi và trừ khi chúng ta dừng lại với dữ liệu trả về (RETURN (opens in a new tab) hoặc REVERT (opens in a new tab)) hoặc không có nó (STOP (opens in a new tab) hoặc SELFDESTRUCT (opens in a new tab)).

Phương trình 143 cho chúng ta biết có bốn điều kiện có thể xảy ra tại mỗi thời điểm trong quá trình thực thi và phải làm gì với chúng:

  1. Z(σ,μ,A,I). Z đại diện cho một hàm kiểm tra xem một hoạt động có tạo ra một quá trình chuyển đổi trạng thái không hợp lệ hay không (xem dừng ngoại lệ). Nếu nó đánh giá là True, trạng thái mới giống hệt trạng thái cũ (ngoại trừ Gas bị đốt cháy) vì các thay đổi chưa được thực hiện.
  2. Nếu mã lệnh đang được thực thi là REVERT (opens in a new tab), trạng thái mới giống như trạng thái cũ, một lượng Gas bị mất.
  3. Nếu chuỗi các hoạt động đã hoàn thành, được biểu thị bằng một RETURN (opens in a new tab)), trạng thái được cập nhật thành trạng thái mới.
  4. Nếu chúng ta không ở một trong các điều kiện kết thúc 1-3, hãy tiếp tục chạy.

9.4.1 Trạng thái máy

Phần này giải thích chi tiết hơn về trạng thái máy. Nó chỉ định rằng w là mã lệnh hiện tại. Nếu μpc nhỏ hơn ||Ib||, độ dài của mã, thì byte đó (Ibpc]) là mã lệnh. Nếu không, mã lệnh được định nghĩa là STOP (opens in a new tab).

Vì đây là một máy ngăn xếp (opens in a new tab), chúng ta cần theo dõi số lượng mục được lấy ra (δ) và đẩy vào (α) bởi mỗi mã lệnh.

9.4.2 Dừng ngoại lệ

Phần này định nghĩa hàm Z, chỉ định khi nào chúng ta có một sự kết thúc bất thường. Đây là một hàm Boolean (opens in a new tab), vì vậy nó sử dụng cho phép toán logic OR (opens in a new tab) cho phép toán logic AND (opens in a new tab).

Chúng ta có một điểm dừng ngoại lệ nếu bất kỳ điều kiện nào sau đây là đúng:

  • μg < C(σ,μ,A,I) Như chúng ta đã thấy trong phần 9.2, C là hàm chỉ định chi phí Gas. Không còn đủ Gas để trang trải cho mã lệnh tiếp theo.

  • δw=∅ Nếu số lượng mục được lấy ra cho một mã lệnh không được xác định, thì bản thân mã lệnh đó cũng không được xác định.

  • || μs || < δw Tràn dưới ngăn xếp (stack underflow), không đủ mục trong ngăn xếp cho mã lệnh hiện tại.

  • w = JUMP ∧ μs[0]∉D(Ib) Mã lệnh là JUMP (opens in a new tab) và Địa chỉ không phải là JUMPDEST (opens in a new tab). Các bước nhảy chỉ hợp lệ khi đích đến là một JUMPDEST (opens in a new tab).

  • w = JUMPI ∧ μs[1]≠0 ∧ μs[0] ∉ D(Ib) Mã lệnh là JUMPI (opens in a new tab), điều kiện là đúng (khác 0) nên bước nhảy sẽ xảy ra và Địa chỉ không phải là JUMPDEST (opens in a new tab). Các bước nhảy chỉ hợp lệ khi đích đến là một JUMPDEST (opens in a new tab).

  • w = RETURNDATACOPY ∧ μs[1]+μs[2]>|| μo || Mã lệnh là RETURNDATACOPY (opens in a new tab). Trong mã lệnh này, phần tử ngăn xếp μs[1] là độ lệch (offset) để đọc từ bộ đệm dữ liệu trả về và phần tử ngăn xếp μs[2] là độ dài của dữ liệu. Điều kiện này xảy ra khi bạn cố gắng đọc vượt quá phần cuối của bộ đệm dữ liệu trả về. Lưu ý rằng không có điều kiện tương tự cho dữ liệu lệnh gọi (calldata) hoặc cho chính mã đó. Khi bạn cố gắng đọc vượt quá phần cuối của các bộ đệm đó, bạn chỉ nhận được các số 0.

  • || μs || - δw + αw > 1024

    Tràn số ngăn xếp (stack overflow). Nếu việc chạy mã lệnh sẽ dẫn đến một ngăn xếp có hơn 1024 mục, hãy hủy bỏ.

  • ¬Iw ∧ W(w,μ) Chúng ta có đang chạy tĩnh không (¬ là phủ định (opens in a new tab)Iw là đúng khi chúng ta được phép thay đổi trạng thái Chuỗi khối)? Nếu vậy, và chúng ta đang thử một hoạt động thay đổi trạng thái, điều đó không thể xảy ra.

    Hàm W(w,μ) được định nghĩa sau trong phương trình 150. W(w,μ) là đúng nếu một trong các điều kiện sau là đúng:

    • w ∈ {CREATE, CREATE2, SSTORE, SELFDESTRUCT} Các mã lệnh này thay đổi trạng thái, bằng cách tạo một hợp đồng mới, lưu trữ một giá trị hoặc tự hủy hợp đồng hiện tại.

    • LOG0≤w ∧ w≤LOG4 Nếu chúng ta được gọi tĩnh, chúng ta không thể phát ra các mục Nhật ký. Các mã lệnh Nhật ký đều nằm trong phạm vi từ LOG0 (A0) (opens in a new tab) đến LOG4 (A4) (opens in a new tab). Số sau mã lệnh Nhật ký chỉ định số lượng chủ đề mà mục Nhật ký chứa.

    • w=CALL ∧ μs[2]≠0 Bạn có thể gọi một hợp đồng khác khi bạn đang ở trạng thái tĩnh, nhưng nếu bạn làm vậy, bạn không thể chuyển ETH cho nó.

  • w = SSTORE ∧ μg ≤ Gcallstipend Bạn không thể chạy SSTORE (opens in a new tab) trừ khi bạn có nhiều hơn Gcallstipend (được định nghĩa là 2300 trong Phụ lục G) Gas.

9.4.3 Tính hợp lệ của đích đến bước nhảy

Ở đây chúng ta chính thức định nghĩa các mã lệnh JUMPDEST (opens in a new tab) là gì. Chúng ta không thể chỉ tìm kiếm giá trị byte 0x5B, vì nó có thể nằm bên trong một PUSH (và do đó là dữ liệu chứ không phải mã lệnh).

Trong phương trình (153), chúng ta định nghĩa một hàm, N(i,w). Tham số đầu tiên, i, là vị trí của mã lệnh. Tham số thứ hai, w, là chính mã lệnh đó. Nếu w∈[PUSH1, PUSH32] điều đó có nghĩa là mã lệnh là một PUSH (dấu ngoặc vuông xác định một phạm vi bao gồm các điểm cuối). Trong trường hợp đó, mã lệnh tiếp theo nằm ở i+2+(w−PUSH1). Đối với PUSH1 (opens in a new tab), chúng ta cần tiến lên hai byte (bản thân PUSH và giá trị một byte), đối với PUSH2 (opens in a new tab), chúng ta cần tiến lên ba byte vì nó là giá trị hai byte, v.v. Tất cả các mã lệnh EVM khác chỉ dài một byte, vì vậy trong tất cả các trường hợp khác N(i,w)=i+1.

Hàm này được sử dụng trong phương trình (152) để định nghĩa DJ(c,i), là tập hợp (opens in a new tab) của tất cả các đích đến bước nhảy hợp lệ trong mã c, bắt đầu từ vị trí mã lệnh i. Hàm này được định nghĩa đệ quy. Nếu i≥||c||, điều đó có nghĩa là chúng ta đang ở tại hoặc sau phần cuối của mã. Chúng ta sẽ không tìm thấy bất kỳ đích đến bước nhảy nào nữa, vì vậy chỉ cần trả về tập hợp rỗng.

Trong tất cả các trường hợp khác, chúng ta xem xét phần còn lại của mã bằng cách chuyển đến mã lệnh tiếp theo và lấy tập hợp bắt đầu từ nó. c[i] là mã lệnh hiện tại, vì vậy N(i,c[i]) là vị trí của mã lệnh tiếp theo. Do đó, DJ(c,N(i,c[i])) là tập hợp các đích đến bước nhảy hợp lệ bắt đầu từ mã lệnh tiếp theo. Nếu mã lệnh hiện tại không phải là JUMPDEST, chỉ cần trả về tập hợp đó. Nếu nó là JUMPDEST, hãy đưa nó vào tập kết quả và trả về tập hợp đó.

9.4.4 Dừng bình thường

Hàm dừng H, có thể trả về ba loại giá trị.

  • Nếu chúng ta không ở trong một mã lệnh dừng, trả về , tập hợp rỗng. Theo quy ước, giá trị này được hiểu là Boolean false.
  • Nếu chúng ta có một mã lệnh dừng không tạo ra đầu ra (hoặc STOP (opens in a new tab) hoặc SELFDESTRUCT (opens in a new tab)), trả về một chuỗi có kích thước 0 byte làm giá trị trả về. Lưu ý rằng điều này rất khác với tập hợp rỗng. Giá trị này có nghĩa là EVM thực sự đã dừng lại, chỉ là không có dữ liệu trả về để đọc.
  • Nếu chúng ta có một mã lệnh dừng tạo ra đầu ra (hoặc RETURN (opens in a new tab) hoặc REVERT (opens in a new tab)), trả về chuỗi byte được chỉ định bởi mã lệnh đó. Chuỗi này được lấy từ bộ nhớ, giá trị ở đỉnh ngăn xếp (μs[0]) là byte đầu tiên và giá trị sau nó (μs[1]) là độ dài.

H.2 Tập lệnh

Trước khi chúng ta đi đến tiểu mục cuối cùng của EVM, 9.5, hãy xem xét chính các lệnh. Chúng được định nghĩa trong Phụ lục H.2 bắt đầu từ trang 29. Bất cứ điều gì không được chỉ định là thay đổi với mã lệnh cụ thể đó thì được cho là sẽ giữ nguyên. Các biến có thay đổi được chỉ định bằng <something>′.

Ví dụ, hãy xem xét mã lệnh ADD (opens in a new tab).

Giá trịGợi nhớδαMô tả
0x01ADD21Phép cộng.
μ′s[0] ≡ μs[0] + μs[1]

δ là số lượng giá trị chúng ta lấy ra từ ngăn xếp. Trong trường hợp này là hai, vì chúng ta đang cộng hai giá trị trên cùng.

α là số lượng giá trị chúng ta đẩy trở lại. Trong trường hợp này là một, tổng số.

Vì vậy, đỉnh ngăn xếp mới (μ′s[0]) là tổng của đỉnh ngăn xếp cũ (μs[0]) và giá trị cũ bên dưới nó (μs[1]).

Thay vì xem qua tất cả các mã lệnh với một "danh sách dài lê thê", bài viết này chỉ giải thích những mã lệnh giới thiệu một cái gì đó mới.

Giá trịGợi nhớδαMô tả
0x20KECCAK25621Tính toán Mã băm Keccak-256.
μ′s[0] ≡ KEC(μms[0] . . . (μs[0] + μs[1] − 1)])
μ′i ≡ M(μis[0],μs[1])

Đây là mã lệnh đầu tiên truy cập bộ nhớ (trong trường hợp này, chỉ đọc). Tuy nhiên, nó có thể mở rộng vượt quá giới hạn hiện tại của bộ nhớ, vì vậy chúng ta cần cập nhật μi. Chúng ta làm điều này bằng cách sử dụng hàm M được định nghĩa trong phương trình 328 trên trang 29.

Giá trịGợi nhớδαMô tả
0x31BALANCE11Lấy số dư của Tài khoản đã cho.
...

Địa chỉ mà chúng ta cần tìm số dư là μs[0] mod 2160. Đỉnh của ngăn xếp là Địa chỉ, nhưng vì các Địa chỉ chỉ có 160 bit, chúng ta tính giá trị modulo (opens in a new tab) 2160.

Nếu σ[μs[0] mod 2160] ≠ ∅, điều đó có nghĩa là có thông tin về Địa chỉ này. Trong trường hợp đó, σ[μs[0] mod 2160]b là số dư cho Địa chỉ đó. Nếu σ[μs[0] mod 2160] = ∅, điều đó có nghĩa là Địa chỉ này chưa được khởi tạo và số dư bằng 0. Bạn có thể xem danh sách các trường thông tin Tài khoản trong phần 4.1 trên trang 4.

Phương trình thứ hai, A'a ≡ Aa ∪ {μs[0] mod 2160}, liên quan đến sự khác biệt về chi phí giữa việc truy cập vào bộ lưu trữ ấm (bộ lưu trữ đã được truy cập gần đây và có khả năng được lưu trong bộ nhớ cache) và bộ lưu trữ lạnh (bộ lưu trữ chưa được truy cập và có khả năng nằm trong bộ lưu trữ chậm hơn, tốn kém hơn để truy xuất). Aa là danh sách các Địa chỉ đã được giao dịch truy cập trước đó, do đó sẽ rẻ hơn để truy cập, như được định nghĩa trong phần 6.1 trên trang 8. Bạn có thể đọc thêm về chủ đề này trong EIP-2929 (opens in a new tab).

Giá trịGợi nhớδαMô tả
0x8FDUP161617Nhân bản mục ngăn xếp thứ 16.
μ′s[0] ≡ μs[15]

Lưu ý rằng để sử dụng bất kỳ mục ngăn xếp nào, chúng ta cần lấy nó ra, điều đó có nghĩa là chúng ta cũng cần lấy ra tất cả các mục ngăn xếp nằm trên nó. Trong trường hợp của DUP<n> (opens in a new tab)SWAP<n> (opens in a new tab), điều này có nghĩa là phải lấy ra và sau đó đẩy vào tối đa mười sáu giá trị.

9.5 Chu kỳ thực thi

Bây giờ chúng ta đã có tất cả các phần, cuối cùng chúng ta có thể hiểu cách chu kỳ thực thi của EVM được ghi lại.

Phương trình (155) nói rằng với trạng thái:

  • σ (trạng thái Chuỗi khối toàn cầu)
  • μ (trạng thái EVM)
  • A (trạng thái phụ, các thay đổi sẽ xảy ra khi giao dịch kết thúc)
  • I (môi trường thực thi)

Trạng thái mới là (σ', μ', A', I').

Các phương trình (156)-(158) định nghĩa ngăn xếp và sự thay đổi trong đó do một mã lệnh (μs). Phương trình (159) là sự thay đổi về Gas (μg). Phương trình (160) là sự thay đổi trong bộ đếm chương trình (μpc). Cuối cùng, các phương trình (161)-(164) chỉ định rằng các tham số khác giữ nguyên, trừ khi được thay đổi rõ ràng bởi mã lệnh.

Với điều này, EVM được định nghĩa đầy đủ.

Kết luận

Ký hiệu toán học rất chính xác và đã cho phép Sách vàng chỉ định mọi chi tiết của Ethereum. Tuy nhiên, nó có một số nhược điểm:

  • Nó chỉ có thể được con người hiểu, điều đó có nghĩa là các bài kiểm tra tuân thủ (opens in a new tab) phải được viết thủ công.
  • Các lập trình viên hiểu mã máy tính. Họ có thể hiểu hoặc không hiểu ký hiệu toán học.

Có lẽ vì những lý do này, các đặc tả lớp đồng thuận (opens in a new tab) mới hơn được viết bằng Python. Có các đặc tả lớp thực thi bằng Python (opens in a new tab), nhưng chúng chưa hoàn chỉnh. Cho đến khi và trừ khi toàn bộ Sách vàng cũng được dịch sang Python hoặc một ngôn ngữ tương tự, Sách vàng sẽ tiếp tục được sử dụng và việc có thể đọc nó là rất hữu ích.