Tóm tắt
Các giao dịch trải qua 8 giai đoạn: nhận, xác minh chữ ký, kiểm tra tính hợp lệ, kiểm tra ngân sách/thời gian, xác thực người trả phí, tải tài khoản, thực thi lệnh và commit.
Quy trình xử lý giao dịch
Khi một giao dịch đến validator, nó sẽ trải qua một loạt các giai đoạn xác thực và thực thi. Phần sau mô tả quy trình đầy đủ từ việc nhận đến commit, với các tham chiếu tệp nguồn vào agave validator client.
1. Nhận và giải mã
Validator nhận các byte giao dịch qua UDP/QUIC. Các byte thô phải vừa trong một
gói tin duy nhất (PACKET_DATA_SIZE = 1.232 byte). Các byte được giải mã
thành một VersionedTransaction, chứa mảng chữ ký và một
VersionedMessage (legacy hoặc v0).
2. Xác minh chữ ký (sigverify)
Các chữ ký được xác minh trong
giai đoạn sigverify
trước khi giao dịch vào giai đoạn banking. Với mỗi chữ ký tại chỉ số i, trình
xác minh kiểm tra Ed25519(signatures[i], account_keys[i], message_bytes).
Nếu bất kỳ chữ ký nào không hợp lệ, gói tin sẽ bị loại bỏ.
Việc xác minh được song song hóa: validator chia các batch gói tin thành các
chunk
VERIFY_PACKET_CHUNK_SIZE
(128) và xử lý chúng song song.
3. Kiểm tra tính hợp lệ
Giao dịch đã giải mã được kiểm tra để tạo ra một SanitizedTransaction
(hoặc
RuntimeTransaction).
Việc kiểm tra xác thực các bất biến cấu trúc:
- Số lượng chữ ký khớp với
num_required_signaturestrong header - Tất cả
program_id_indexvàaccount_indicescủa lệnh đều nằm trong giới hạn - Người trả phí (chỉ số tài khoản 0) là một signer có thể ghi
Wrapper RuntimeTransaction lưu trữ metadata được tính toán trước từ
TransactionMeta:
message hash, cờ giao dịch vote, số lượng chữ ký precompile
(Ed25519/secp256k1/secp256r1), chi tiết lệnh compute budget và tổng độ dài dữ
liệu lệnh.
4. Kiểm tra ngân sách tính toán, tuổi và bộ nhớ đệm trạng thái
Phương thức
check_transactions
thực hiện một số kiểm tra cho mỗi giao dịch:
Ngân sách tính toán: Các chỉ thị ngân sách tính toán của giao dịch được phân
tích và xác thực trước. Chi tiết phí được tính toán từ giới hạn ngân sách và phí
ưu tiên. Nếu ngân sách tính toán không hợp lệ hoặc xung đột, giao dịch sẽ thất
bại với các lỗi phân tích ngân sách tính toán như DuplicateInstruction,
InstructionError(..., InvalidInstructionData), hoặc
InvalidLoadedAccountsDataSizeLimit.
Tuổi blockhash: recent_blockhash của giao dịch được tra cứu trong
BlockhashQueue.
Nếu tìm thấy hash và tuổi của nó nằm trong MAX_PROCESSING_AGE (150 slot),
giao dịch sẽ tiếp tục. Nếu không tìm thấy, validator sẽ kiểm tra
durable nonce hợp lệ.
Bộ nhớ đệm trạng thái: Hash thông điệp của giao dịch được kiểm tra với bộ
nhớ đệm trạng thái. Nếu tìm thấy, giao dịch sẽ bị từ chối với
AlreadyProcessed.
5. Xác thực nonce và người trả phí
Phương thức
validate_transaction_nonce_and_fee_payer
trong SVM xử lý hai xác thực:
Xác thực nonce (nếu có): Đối với các giao dịch nonce, validator tải tài khoản nonce và xác minh:
- Tài khoản thuộc sở hữu của System Program
- Nó được phân tích là
State::Initialized - Durable nonce được lưu trữ khớp với
recent_blockhashcủa giao dịch - Nonce có thể được nâng cấp (durable nonce hiện tại khác với durable nonce tiếp theo, tức là nonce chưa được sử dụng trong block hiện tại)
- Quyền nonce đã ký giao dịch
Nếu hợp lệ, nonce được nâng cấp lên giá trị durable nonce tiếp theo. Xem
validate_transaction_nonce.
Xác thực người trả phí: Tài khoản người trả phí (luôn là chỉ số 0) được tải
và kiểm tra bởi
validate_fee_payer:
- Tài khoản phải tồn tại (lamports > 0), nếu không sẽ báo
AccountNotFound - Tài khoản phải là tài khoản hệ thống hoặc tài khoản nonce, nếu không sẽ báo
InvalidAccountForFee - Lamports phải đủ để chi trả
min_balance + total_fee, trong đómin_balancelà 0 đối với tài khoản hệ thống hoặcrent.minimum_balance(NonceState::size())đối với tài khoản nonce; nếu không sẽ báoInsufficientFundsForFee - Sau khi khấu trừ phí, tài khoản phải vẫn được miễn phí thuê (không thể chuyển từ miễn phí thuê sang phải trả phí thuê)
Phí được khấu trừ từ người trả phí ở giai đoạn này. Một bản chụp nhanh của người
trả phí đã trừ phí (và nonce nâng cao, nếu có) được lưu dưới dạng
RollbackAccounts, đây là các tài khoản được cam kết ngay cả khi việc thực
thi thất bại.
6. Tải tài khoản
load_transaction
tải tất cả các tài khoản được tham chiếu bởi giao dịch.
AccountLoader
bao bọc kho lưu trữ tài khoản bên ngoài và duy trì bộ nhớ đệm cục bộ theo lô để
các tài khoản được sửa đổi bởi các giao dịch trước đó trong cùng một lô có thể
hiển thị cho các giao dịch sau đó.
Đối với mỗi tài khoản không phải người trả phí, trình tải:
- Lấy tài khoản từ bộ nhớ đệm hoặc accounts-db
- Cập nhật trạng thái miễn phí thuê nếu cần
- Tích lũy kích thước dữ liệu của tài khoản vào
loaded_accounts_data_size_limit(mặc định 64 MiB). Mỗi tài khoản phát sinh chi phí cơ bản làTRANSACTION_ACCOUNT_BASE_SIZE(64 byte) cộng với độ dài dữ liệu của nó
Đối với mỗi chương trình được gọi bởi các lệnh của giao dịch, trình tải xác minh
rằng tài khoản chương trình tồn tại và thuộc sở hữu của một trình tải hợp lệ
(NativeLoader hoặc một trong các PROGRAM_OWNERS). Các chương trình không
hợp lệ sẽ thất bại với ProgramAccountNotFound hoặc
InvalidProgramForExecution.
Các chương trình LoaderV3 (có thể nâng cấp) ngầm định tải tài khoản programdata liên quan của chúng, cũng được tính vào giới hạn kích thước dữ liệu đã tải.
Nếu việc tải tài khoản thất bại nhưng người trả phí đã được xác thực thành công,
giao dịch sẽ trở thành kết quả
FeesOnly:
phí vẫn được thu nhưng không có lệnh nào được thực thi.
7. Thực thi lệnh
execute_loaded_transaction
tạo một TransactionContext với tất cả các tài khoản đã tải và gọi
process_message.
Các lệnh được thực thi tuần tự theo thứ tự chúng xuất hiện trong thông điệp. Mỗi
lần gọi lệnh tạo một InvokeContext và gọi chương trình mục tiêu.
Chi tiết xử lý lệnh
Hàm
process_message
của runtime lặp qua từng lệnh và gọi chương trình mục tiêu:
- Đối với mỗi instruction, runtime gọi
prepare_next_top_level_instruction, hàm này xây dựngInstructionContext. Context này chứa các tham chiếu đến các account của instruction (được phân giải từ các chỉ mục đã biên dịch), instruction data và chỉ mục program account. - Runtime kiểm tra xem program có phải là precompile (Ed25519, Secp256k1, Secp256r1) hay không. Các precompile được xác minh trực tiếp mà không cần gọi BPF VM.
- Đối với tất cả các program khác, runtime gọi
process_instruction, hàm này tải program từ cache và thực thi nó trong máy ảo BPF. - Sau khi instruction hoàn thành, runtime
xác minh
rằng tổng số dư lamport trên tất cả các instruction account không thay đổi
(kiểm tra
UnbalancedInstruction). - Nếu bất kỳ instruction nào thất bại, toàn bộ transaction sẽ được rollback. Không có thay đổi trạng thái trung gian nào được commit.
Mỗi instruction tăng instruction trace. Trace bao gồm cả các instruction cấp cao
nhất và bất kỳ CPI nào mà chúng gọi. Tổng độ dài trace (các
instruction cấp cao nhất cộng với tất cả các CPI lồng nhau) không được vượt quá
64 (MAX_INSTRUCTION_TRACE_LENGTH). Vượt quá giới hạn này sẽ trả về
InstructionError::MaxInstructionTraceLengthExceeded.
Sau khi thực thi, runtime xác minh rằng:
- Tổng số lamport trên tất cả các account không thay đổi
- Không có account nào chuyển từ trạng thái miễn phí rent sang trạng thái phải trả rent
8. Commit hoặc rollback
Nếu thực thi thành công, các trạng thái account đã sửa đổi từ
TransactionContext được ghi lại vào cache cục bộ theo batch của
AccountLoader. Nếu thực thi thất bại, chỉ có RollbackAccounts (fee
payer với phí đã khấu trừ và nonce đã tăng) được ghi lại. Phí vẫn được thu,
nhưng tất cả các thay đổi account khác đều bị loại bỏ.
Tóm tắt pipeline
Receive packet (UDP/QUIC)--> Deserialize into VersionedTransaction--> Sigverify (parallel Ed25519 verification)--> Sanitize (structural validation, metadata extraction)--> Parse compute budget, calculate fees--> Check blockhash age (or verify nonce account)--> Check status cache (dedup)--> Validate nonce authority and advanceability (if nonce transaction)--> Validate fee payer (load, check balance, deduct fee)--> Load all accounts (with data size limits)--> Load programs (verify loaders)--> Execute instructions sequentially--> Verify post-conditions (lamport balance, rent state)--> Commit account changes (or rollback on failure)
Tham chiếu lỗi transaction
Bảng sau liệt kê tất cả các biến thể
TransactionError
và giai đoạn pipeline mà chúng xảy ra:
| Lỗi | Giai đoạn | Nguyên nhân |
|---|---|---|
AccountInUse | Lập lịch | Tài khoản đã bị khóa bởi giao dịch khác trong cùng một lô |
AccountLoadedTwice | Lập lịch | Một pubkey xuất hiện hai lần trong account_keys của giao dịch |
AccountNotFound | Xác thực người trả phí | Tài khoản người trả phí không tồn tại |
ProgramAccountNotFound | Tải tài khoản | Chương trình được gọi không tồn tại |
InsufficientFundsForFee | Xác thực người trả phí | Người trả phí không đủ để chi trả phí + mức tối thiểu miễn rent |
InvalidAccountForFee | Xác thực người trả phí | Người trả phí không phải là tài khoản hệ thống hoặc tài khoản nonce |
AlreadyProcessed | Bộ nhớ đệm trạng thái | Giao dịch đã được xử lý trước đó |
BlockhashNotFound | Kiểm tra tuổi | Blockhash không có trong hàng đợi và không phải là nonce hợp lệ |
InstructionError | Thực thi | Đã xảy ra lỗi khi xử lý một lệnh (bao gồm chỉ số lệnh và InstructionError cụ thể) |
CallChainTooDeep | Tải tài khoản | Chuỗi gọi loader quá sâu |
MissingSignatureForFee | Sanitize | Giao dịch yêu cầu phí nhưng không có chữ ký |
InvalidAccountIndex | Sanitize | Giao dịch chứa tham chiếu tài khoản không hợp lệ |
SignatureFailure | Sigverify | Chữ ký Ed25519 không xác minh được (gói tin bị loại bỏ) |
InvalidProgramForExecution | Tải tài khoản | Chương trình không thuộc sở hữu của loader hợp lệ |
SanitizeFailure | Sanitize | Giao dịch không sanitize được offset tài khoản một cách chính xác |
ClusterMaintenance | Lập lịch | Giao dịch hiện đang bị vô hiệu hóa do bảo trì cластер |
AccountBorrowOutstanding | Thực thi | Xử lý giao dịch để lại tài khoản với tham chiếu mượn chưa giải quyết |
WouldExceedMaxBlockCostLimit | Lập lịch | Giao dịch sẽ vượt quá giới hạn chi phí khối tối đa |
UnsupportedVersion | Sanitize | Phiên bản giao dịch không được hỗ trợ |
InvalidWritableAccount | Tải tài khoản | Giao dịch tải tài khoản có thể ghi nhưng không thể ghi được |
WouldExceedMaxAccountCostLimit | Lập lịch | Giao dịch sẽ vượt quá giới hạn chi phí tài khoản tối đa trong khối |
WouldExceedAccountDataBlockLimit | Lập lịch | Giao dịch sẽ vượt quá giới hạn dữ liệu tài khoản trong khối |
TooManyAccountLocks | Lập lịch | Giao dịch khóa quá nhiều tài khoản |
AddressLookupTableNotFound | Tải tài khoản | Tài khoản bảng tra cứu địa chỉ không tồn tại |
InvalidAddressLookupTableOwner | Tải tài khoản | Bảng tra cứu địa chỉ thuộc sở hữu của chương trình sai |
InvalidAddressLookupTableData | Tải tài khoản | Bảng tra cứu địa chỉ chứa dữ liệu không hợp lệ |
InvalidAddressLookupTableIndex | Tải tài khoản | Tra cứu bảng địa chỉ sử dụng chỉ số không hợp lệ |
InvalidRentPayingAccount | Kiểm tra sau thực thi | Tài khoản chuyển từ miễn rent sang phải trả rent |
WouldExceedMaxVoteCostLimit | Lập lịch | Giao dịch sẽ vượt quá giới hạn chi phí bỏ phiếu tối đa |
WouldExceedAccountDataTotalLimit | Lập lịch | Giao dịch sẽ vượt quá tổng giới hạn dữ liệu tài khoản |
DuplicateInstruction | Phân tích ngân sách tính toán | Biến thể lệnh ngân sách tính toán trùng lặp trong cùng một giao dịch |
InsufficientFundsForRent | Kiểm tra sau thực thi | Tài khoản không có đủ lamport để chi trả rent cho kích thước dữ liệu của nó |
MaxLoadedAccountsDataSizeExceeded | Tải tài khoản | Tổng dữ liệu đã tải vượt quá giới hạn 64 MiB |
InvalidLoadedAccountsDataSizeLimit | Phân tích ngân sách tính toán | SetLoadedAccountsDataSizeLimit được đặt thành 0 |
ResanitizationNeeded | Sanitize | Giao dịch khác nhau trước/sau khi kích hoạt tính năng và cần sanitize lại |
ProgramExecutionTemporarilyRestricted | Tải tài khoản | Thực thi chương trình tạm thời bị hạn chế trên tài khoản được tham chiếu |
UnbalancedTransaction | Kiểm tra sau thực thi | Tổng số dư lamport trước giao dịch không bằng số dư sau |
ProgramCacheHitMaxLimit | Tải tài khoản | Bộ nhớ đệm chương trình đạt giới hạn tối đa |
CommitCancelled | Commit | Commit bị hủy nội bộ |
Is this page helpful?