Tóm tắt
Các chương trình được biên dịch thành sBPF thông qua LLVM và chạy trong VM sandbox với ngân sách 1.4M CU cho mỗi giao dịch. Runtime lưu đệm tối đa 512 chương trình đã biên dịch, cung cấp syscalls cho logging, CPI, crypto và bộ nhớ, đồng thời trì hoãn các triển khai mới 1 slot.
Biên dịch
Solana sử dụng LLVM để biên dịch các chương trình thành các tệp nhị phân ELF chứa Solana Bytecode Format (sBPF). Tệp nhị phân ELF được lưu trữ on-chain trong một tài khoản thực thi.
sBPF là biến thể tùy chỉnh của Solana dựa trên bytecode eBPF, được điều chỉnh cho Solana runtime. Nó không phải là eBPF chuẩn và có các sửa đổi đặc thù của Solana.
Viết chương trình
Các chương trình Solana chủ yếu được viết bằng Rust sử dụng một trong hai cách tiếp cận:
Anchor
Một framework sử dụng các macro Rust để giảm boilerplate. Được khuyến nghị cho hầu hết các nhà phát triển.
Native Rust
Rust trực tiếp không dùng framework. Cung cấp toàn quyền kiểm soát nhưng yêu cầu nhiều triển khai thủ công hơn.
Mô hình thực thi chương trình
Khi một giao dịch được xử lý, runtime thực thi từng lệnh tuần tự thông qua
process_message().
Đối với mỗi lệnh, runtime:
-
Chuẩn bị ngữ cảnh lệnh. Gọi
prepare_next_top_level_instruction()để ánh xạ các chỉ số tài khoản của lệnh, đặt các cờ signer và writable, và cấu hìnhTransactionContext. -
Kiểm tra precompiles. Nếu chương trình là một precompile, runtime gọi
process_precompile(), vẫn đẩy và lấy ra một stack frame (thông quapush()vàpop()) nhưng bỏ qua sBPF VM và tra cứu bộ nhớ đệm chương trình, thực thi mã native trực tiếp. -
Đẩy một stack frame. (Các bước 3-6 xảy ra bên trong
InvokeContext::process_instruction()vàprocess_executable_chain(), được gọi từprocess_message().) Gọipush()trênInvokeContext, điều này tăng chiều cao của instruction stack và thực thi quy tắc reentrancy: một program chỉ có thể tự gọi lại chính nó nếu caller trực tiếp (program ở đỉnh hiện tại của instruction stack) là cùng một program. Đệ quy sâu (A -> A -> A) được cho phép, tuân theo giới hạn độ sâu stack. Các pattern reentrancy khác (ví dụ: A gọi B gọi A) trả vềInstructionError::ReentrancyNotAllowed. -
Phân giải program. Runtime gọi
process_executable_chain()để xác định loader. Nếu owner của program account là native loader, program đó là builtin và hàm entrypoint của nó được tra cứu trực tiếp từProgramCacheForTxBatch. Nếu owner là một trong các BPF loader (bpf_loader_deprecated,bpf_loader,bpf_loader_upgradeable, hoặcloader_v4), entrypoint builtin của loader sẽ được gọi thay thế. -
Thực thi BPF program. Đối với các BPF program, loader entrypoint tra cứu executable đã biên dịch từ program cache.
execute()sau đó:- Serialize dữ liệu account thành một parameter buffer phẳng
- Tạo sBPF VM với các vùng stack, heap và memory
- Chạy code đã biên dịch, tiêu thụ compute unit trong quá trình thực thi. Trả
về
ComputationalBudgetExceedednếu vượt quá ngân sách. - Deserialize dữ liệu account từ buffer trở lại thành trạng thái account
-
Loại bỏ stack frame. Gọi
pop()để xác minh rằng instruction không vi phạm các quy tắc kế toán của runtime (số dư lamport cân bằng, các account readonly không bị sửa đổi, kích thước dữ liệu account nằm trong giới hạn). -
Tích lũy compute unit. Các compute unit được tiêu thụ bởi instruction được cộng vào tổng số của transaction thông qua
saturating_add.
Bộ nhớ đệm chương trình
Runtime duy trì một
ProgramCache
toàn cục lưu trữ các chương trình đã được xác minh và biên dịch. Nó nhận biết
fork-graph và xử lý các quy tắc hiển thị triển khai, loại bỏ và biên dịch lại
tại ranh giới epoch.
Các loại mục trong bộ nhớ đệm
Mỗi chương trình được lưu trong bộ nhớ đệm có một
ProgramCacheEntryType
xác định hành vi runtime của nó:
| Loại | Mô tả |
|---|---|
Loaded | Chương trình đã được xác minh và biên dịch, sẵn sàng để thực thi. |
Builtin | Chương trình native được biên dịch vào binary của validator (System, Stake, Vote, v.v.). Không được lưu trữ on-chain. |
Unloaded | Chương trình đã được xác minh trước đó nhưng tệp thực thi đã biên dịch bị loại bỏ khỏi bộ nhớ để giải phóng không gian. Vẫn theo dõi thống kê sử dụng. Có thể được tải lại mà không cần xác minh lại. |
FailedVerification | Tombstone cho các chương trình không vượt qua trình xác minh sBPF theo tập tính năng hiện tại. Có thể trở thành Loaded nếu các kích hoạt tính năng thay đổi quy tắc xác minh. |
Closed | Tombstone cho các chương trình đã bị đóng rõ ràng hoặc chưa bao giờ được triển khai. Cũng được sử dụng cho các tài khoản (như tài khoản buffer) thuộc về một loader nhưng không chứa mã thực thi. |
DelayVisibility | Tombstone tổng hợp được trả về bởi ProgramCacheForTxBatch::find() khi một mục Loaded tồn tại nhưng chưa có hiệu lực (effective_slot của nó nằm trong tương lai). Không bao giờ được lưu trực tiếp trong bộ nhớ đệm. |
Độ trễ hiển thị
Các chương trình mới được triển khai hoặc nâng cấp không có hiệu lực ngay lập
tức. Hằng số
DELAY_VISIBILITY_SLOT_OFFSET
là 1, có nghĩa là một chương trình được triển khai trong slot N sẽ có hiệu lực
ở slot N+1. Trong slot triển khai, bất kỳ nỗ lực nào để gọi phiên bản mới sẽ trả
về DelayVisibility, khiến runtime báo cáo "Program is not deployed."
Chính sách loại bỏ
Bộ nhớ cache lưu trữ tối đa
MAX_LOADED_ENTRY_COUNT
(512) mục chương trình đã biên dịch. Khi đạt đến giới hạn, các chương trình ít
được sử dụng nhất sẽ bị loại bỏ về trạng thái Unloaded. Mức độ sử dụng
được theo dõi bởi
tx_usage_counter
(tăng lên mỗi khi một giao dịch tham chiếu đến chương trình) và
latest_access_slot.
Biên dịch lại tại ranh giới epoch
Nếu việc kích hoạt tính năng thay đổi
ProgramRuntimeEnvironments
tại ranh giới epoch, tất cả các chương trình được lưu trong cache sẽ được
biên dịch lại
đối với môi trường mới.
Dữ liệu trả về
Các chương trình có thể thiết lập dữ liệu trả về thông qua syscall
sol_set_return_data. Dữ liệu được lưu trữ trong một cấu trúc
TransactionReturnData
cấp độ giao dịch chứa các byte dữ liệu và program_id của chương trình có lệnh
gọi syscall. Kích thước tối đa là 1.024 byte (MAX_RETURN_DATA).
Is this page helpful?