Podsumowanie
Przed wykonaniem środowisko uruchomieniowe ładuje konta, weryfikuje płatnika opłat, sprawdza zwolnienie z czynszu i serializuje dane kont do układu pamięci, do którego mają dostęp programy.
Ta strona opisuje wewnętrzne mechanizmy środowiska uruchomieniowego. Większość deweloperów nie potrzebuje tych informacji do tworzenia programów. Zobacz Strukturę konta aby poznać perspektywę deweloperską.
Ładowanie konta
Przed wykonaniem transakcji środowisko uruchomieniowe ładuje wszystkie wskazane
konta za pomocą
load_transaction_accounts().
Ten proces wykonuje kilka weryfikacji:
-
Weryfikacja płatnika opłat: Płatnik opłat (pierwsze konto) musi istnieć, być kontem systemowym lub kontem nonce oraz posiadać wystarczającą liczbę lamportów na pokrycie opłat (
validate_fee_payer()). Po opłaceniu opłat konto musi pozostać zwolnione z czynszu lub mieć dokładnie 0 lamportów. Nie może mieć salda pomiędzy 0 a minimalną kwotą zwolnienia z czynszu. Konta nonce zawsze muszą mieć wystarczającą liczbę lamportów, aby pozostać zwolnione z czynszu. Jeśli płatnik nie jest kontem systemowym ani nonce, transakcja kończy się niepowodzeniem zTransactionError::InvalidAccountForFee. -
Limit rozmiaru załadowanych danych: Całkowity rozmiar wszystkich załadowanych kont (w tym
TRANSACTION_ACCOUNT_BASE_SIZE64 bajtów na konto) nie może przekroczyćMAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES(64 MiB). Przekroczenie tego limitu skutkujeTransactionError::MaxLoadedAccountsDataSizeExceeded. -
Weryfikacja konta program account: Każdy program wywoływany przez instrukcję musi istnieć i być własnością prawidłowego loadera: jednego z
PROGRAM_OWNERS(BPF Loader Upgradeable, BPF Loader, BPF Loader Deprecated, Loader V4) lub natywnego loadera. Jeśli konto program account nie istnieje, transakcja kończy się niepowodzeniem zTransactionError::ProgramAccountNotFound. Jeśli istnieje, ale ma nieprawidłowego właściciela, transakcja kończy się niepowodzeniem zTransactionError::InvalidProgramForExecution. -
Nieistniejące konta: Konta, które nie istnieją on-chain, są ładowane jako domyślne konta (0 lamportów, puste dane, właściciel: System Program) z
rent_epochustawionym nau64::MAX.
Format serializacji BPF
Gdy program jest wywoływany, środowisko uruchomieniowe serializuje konta do
ciągłego bufora w pamięci i przekazuje go do BPF VM. Format serializacji
(standardowy format wyrównany używany przez wszystkie loadery poza przestarzałym
loader-v1) jest zdefiniowany w
serialize_parameters_aligned().
Bufor zaczyna się od u64 (8 bajtów, little-endian), zawierającego liczbę kont.
Następnie, dla każdego konta w instrukcji, bufor zawiera:
| Offset | Rozmiar | Pole | Typ |
|---|---|---|---|
| 0 | 1 | znacznik duplikatu | u8 (0xFF = unikalny, index = duplikat tego konta) |
| 1 | 1 | is_signer | u8 (0 lub 1) |
| 2 | 1 | is_writable | u8 (0 lub 1) |
| 3 | 1 | executable | u8 (0 lub 1) |
| 4 | 4 | original_data_len (zarezerwowane, zawsze 0) | [0u8; 4] |
| 8 | 32 | key | Pubkey |
| 40 | 32 | owner | Pubkey |
| 72 | 8 | lamports | u64 (little-endian) |
| 80 | 8 | data_len | u64 (little-endian) |
| 88 | data_len | data | [u8] |
| 88 + data_len | 10240 + padding | miejsce na realloc + wyrównanie | Wypełnione zerami do MAX_PERMITTED_DATA_INCREASE (10 KiB) + padding do wyrównania do BPF_ALIGN_OF_U128 (8 bajtów) |
| ... | 8 | rent_epoch | u64 (little-endian) |
Po wszystkich kontach bufor dołącza:
| Rozmiar | Pole |
|---|---|
| 8 | instruction_data_len (u64, little-endian) |
| instruction_data_len | instruction_data |
| 32 | program_id (Pubkey) |
Dla zduplikowanych kont zapisywany jest tylko 1 bajt (znacznik duplikatu z indeksem oryginału) oraz 7 bajtów wypełnienia.
Deduplikacja kont
Gdy ten sam publiczny klucz konta pojawia się wielokrotnie w tablicy accounts
instrukcji, środowisko wykonawcze
deduplikuje
je. Każdy wpis na liście kont instrukcji otrzymuje własną strukturę
InstructionAccount,
ale wpisy odnoszące się do tego samego konta na poziomie transakcji wskazują na
te same dane bazowe.
Metoda
is_instruction_account_duplicate
określa, czy dany indeks konta instrukcji jest pierwszym wystąpieniem, czy
duplikatem, poprzez wyszukanie indeksu konta na poziomie transakcji i
znalezienie pierwszego indeksu na poziomie instrukcji, który do niego prowadzi:
- Jeśli bieżący indeks konta instrukcji jest równy pierwszemu zmapowanemu
indeksowi, to nie jest duplikatem (zwraca
None). - W przeciwnym razie zwraca
Some(first_index), gdziefirst_indexto indeks pierwszego wystąpienia.
Ponieważ wszystkie odwołania do tego samego konta współdzielą te same dane
bazowe AccountSharedData, modyfikacje przez dowolne odwołanie są
natychmiast widoczne przez wszystkie inne odwołania. Jednak w danym momencie
można posiadać tylko jeden mutowalny zapożyczenie. Próba jednoczesnego
mutowalnego zapożyczenia tego samego konta przez dwa różne indeksy konta
instrukcji zwraca InstructionError::AccountBorrowFailed. Programy muszą
zwolnić jedno zapożyczenie, zanim uzyskają kolejne na tym samym koncie bazowym.
Po wykonaniu programu środowisko wykonawcze deserializuje bufor z powrotem
(deserialize_parameters_aligned())
i stosuje wszelkie zmiany do lamports, data (w tym zmiany długości do
MAX_PERMITTED_DATA_INCREASE) oraz owner.
Is this page helpful?