Podsumowanie
Solana obsługuje trzy formaty transakcji: legacy, v0 i v1. v0 dodaje tablice wyszukiwania adresów (ALT) umożliwiające odwoływanie się do kont za pomocą 1-bajtowych indeksów. v1 zwiększa limit rozmiaru do 4 096 bajtów, przenosi limity zasobów do samej wiadomości i usuwa ALT.
Solana obsługuje trzy formaty transakcji: legacy, v0 i v1. Każdy z nich opisano poniżej w tych samych trzech częściach: układ bajtów w sieci, sposób odwoływania się do kont oraz źródło limitów zasobów.
Status aktywacji v1
Format v1 nie jest jeszcze
aktywny w żadnym klastrze. Aktywacja planowana jest wraz z Agave v4.2.
solana-test-validator w wersji 4.2+ umożliwia lokalne testowanie transakcji v1.
Istniejące aplikacje powinny zapoznać się z przygotowaniem do v1.
Porównanie formatów
| Limit | legacy | v0 | v1 |
|---|---|---|---|
| Maksymalny rozmiar transakcji | 1 232 bajty | 1 232 bajty | 4 096 bajtów |
| Adresy kont | ~32, ograniczone rozmiarem | 64, przez tablice wyszukiwania | 64, bezpośrednio |
| Tablice wyszukiwania adresów | nieobsługiwane | obsługiwane | nieobsługiwane |
| Limity zasobów | Instrukcje ComputeBudget | Instrukcje ComputeBudget | konfiguracja wiadomości |
Format legacy
Oryginalny format, wciąż domyślny w większości narzędzi. Nie ma żadnego prefiksu
wersji: pierwszy bajt transakcji to liczba podpisów zakodowana jako compact-u16,
a pierwszy bajt wiadomości to num_required_signatures, którego najwyższy bit
jest zawsze wyzerowany.
Układ bajtów legacy
| Pole | Rozmiar | Opis |
|---|---|---|
num_signatures | compact-u16 | Liczba podpisów |
signatures | num_signatures x 64 bajty | Podpisy Ed25519 |
header | 3 bajty | MessageHeader — najwyższy bit pierwszego bajtu jest wyzerowany |
num_account_keys | compact-u16 | Liczba kluczy kont |
account_keys | num_account_keys x 32 bajty | Klucze publiczne, wszystkie bezpośrednio |
recent_blockhash | 32 bajty | Specyfikator czasu życia |
num_instructions | compact-u16 | Liczba instrukcji |
instructions | zmienna | Każda instrukcja serializowana ciągiem |
Każda tablica o zmiennej długości poprzedzona jest długością zakodowaną jako compact-u16: 1 bajt dla wartości 0–127, 2–3 bajty dla większych wartości. Układ poszczególnych instrukcji i przykładowe obliczenie rozmiaru znajdziesz w binarnym formacie transakcji.
Konta w formacie legacy
Każde konto zapisywane jest jako pełny 32-bajtowy klucz publiczny w account_keys,
a instrukcje odwołują się do nich za pomocą 1-bajtowego indeksu do tej tablicy.
Nie ma możliwości odwołania się do konta, które nie jest jawnie wymienione
w transakcji — to właśnie ogranicza transakcję legacy do około 32 kont,
zanim wyczerpie limit 1 232 bajtów.
Limity zasobów w formacie legacy
Limit jednostek obliczeniowych, limit rozmiaru danych załadowanych kont, rozmiar sterty i opłata priorytetowa są ustawiane przez dołączenie instrukcji programu ComputeBudget do transakcji. Każda z nich zużywa jeden slot instrukcji i 150 jednostek obliczeniowych. Ich pominięcie jest bezpieczne: środowisko wykonawcze przyjmuje wartości domyślne: 200 000 CU na instrukcję (max 1,4 M), limit danych 64 MiB, stertę 32 KiB i opłatę priorytetową równą zero.
Format V0
v0 to wiadomość legacy z dwoma dodatkami: bajtem prefiksu wersji 0x80 oraz
tablicą address_table_lookups dołączoną po instrukcjach. Wszystko przed tymi
dodatkami jest identyczne bajt po bajcie z formatem legacy.
Układ bajtów V0
| Pole | Rozmiar | Opis |
|---|---|---|
num_signatures | compact-u16 | Liczba podpisów |
signatures | num_signatures x 64 bajty | Podpisy Ed25519 |
0x80 | 1 bajt | Bajt prefiksu wersji — pierwszy bajt wiadomości |
header | 3 bajty | MessageHeader (jak w legacy) |
num_account_keys | compact-u16 | Liczba statycznych kluczy kont |
static_account_keys | num_account_keys x 32 bajty | Klucze występujące dosłownie w transakcji |
recent_blockhash | 32 bajty | Specyfikator czasu życia |
num_instructions | compact-u16 | Liczba instrukcji |
instructions | zmienna | Ten sam format co legacy |
address_table_lookups | compact-u16 + zmienna | Odwołania ALT (patrz poniżej) |
Każdy wpis w tabeli wyszukiwania adresów zawiera:
| Pole | Rozmiar | Opis |
|---|---|---|
account_key | 32 bajty | Klucz publiczny konta ALT |
writable_indexes | compact-u16 + N x 1 bajt | Indeksy do ALT dla kont z prawem zapisu |
readonly_indexes | compact-u16 + N x 1 bajt | Indeksy do ALT dla kont tylko do odczytu |
Tablice wyszukiwania adresów
ALT to konto onchain, które przechowuje do 256 kluczy publicznych. Odwołując się do ALT, transakcja może zawierać dodatkowe konta przy użyciu 1-bajtowych indeksów zamiast 32-bajtowych kluczy publicznych, znacząco redukując narzut na konto.
W czasie wykonywania, zanim rozpocznie się egzekucja, validator rozwiązuje wszystkie odwołania do ALT na pełne klucze publiczne. Rozwiązane adresy są dołączane do statycznych kluczy kont, tworząc pełną listę kluczy kont. Konta rozwiązane przez ALT są uporządkowane tak samo jak konta statyczne: najpierw zapisywalne, potem tylko do odczytu.
Tabele wyszukiwania adresów wpływają jedynie na sposób odwoływania się do kont w transakcji przesyłanej w sieci. Podczas wykonywania, środowisko uruchomieniowe rozwiązuje wszystkie indeksy na pełne adresy kont. Konta rozwiązane przez ALT mogą być tylko zapisywalne lub tylko do odczytu (nie mogą być sygnatariuszami).
Limity zasobów w v0
Bez zmian względem legacy: instrukcje ComputeBudget, z tymi samymi wartościami domyślnymi przy ich pominięciu.
Format V1
v1 zwiększa limit rozmiaru do 4 096 bajtów i restrukturyzuje wiadomość wokół konfiguracji transakcji: limity zasobów są przenoszone z instrukcji ComputeBudget do pól o stałej pozycji w samej wiadomości. Dzięki temu sieć może uszeregować transakcję według opłaty priorytetowej za pomocą jednego odczytu o stałym przesunięciu, zamiast skanować i deserializować listę instrukcji.
Układ bajtów V1
| Pole | Rozmiar | Opis |
|---|---|---|
0x81 | 1 bajt | Bajt prefiksu wersji — pierwszy bajt transakcji |
header | 3 bajty | MessageHeader (identyczny jak w legacy) |
config_mask | 4 bajty | Maska bitowa u32 LE wskazująca, które wartości konfiguracji są obecne |
recent_blockhash | 32 bajty | Specyfikator czasu życia |
num_instructions | 1 bajt | Licznik o stałej szerokości, maks. 64 |
num_addresses | 1 bajt | Licznik o stałej szerokości, maks. 64 |
addresses | N x 32 bajty | Adresy kont, wszystkie bezpośrednio — brak odwołań do tablic wyszukiwania |
config_values | 0–20 bajtów | Jedna wartość na każdy ustawiony bit maski, w kolejności bitów (patrz niżej) |
instruction_headers | N x 4 bajty | Na instrukcję: program_id_index (u8), num_accounts (u8), data_len (u16 LE) |
instruction_payloads | zmienna | Na instrukcję: indeksy kont, następnie instruction data |
signatures | N x 64 bajty | Na końcu, bez prefiksu długości — liczba podpisów pochodzi z nagłówka |
Przy pisaniu dekodera warto zwrócić uwagę na dwie różnice strukturalne względem
legacy i v0. Liczniki są polami u8 o stałej szerokości zamiast compact-u16,
a instrukcje są podzielone na dwa ciągłe bloki: najpierw wszystkie nagłówki
o stałym rozmiarze, a następnie wszystkie ładunki o zmiennej długości, zamiast
kolejnych instrukcji serializowanych ciągiem.
Konta w v1: brak tablic wyszukiwania adresów
v1 celowo usuwa obsługę ALT. 64 surowe adresy to 2 048 bajtów, co swobodnie mieści się w limicie 4 096 bajtów, więc każdy adres jest umieszczony bezpośrednio, podobnie jak w legacy. Jeśli Twoja aplikacja korzysta z tablic wyszukiwania, przejście na v1 oznacza umieszczenie tych adresów bezpośrednio w transakcji.
Limity zasobów w v1: konfiguracja transakcji
Konfiguracja to maska bitowa u32 po której następują wartości o stałej
szerokości dla każdego pola, którego bit jest ustawiony:
| Bit(y) | Pole | Szerokość | Uwagi |
|---|---|---|---|
| 0–1 | Opłata priorytetowa | u64 | Łączna liczba lamport — oba bity ustawiane razem |
| 2 | Limit jednostek obliczeniowych | u32 | |
| 3 | Limit rozmiaru danych załadowanych kont | u32 | |
| 4 | Żądany rozmiar sterty | u32 |
Nieznane bity są odrzucane. Ponieważ wiadomość jest podpisana, nierozpoznanych pól konfiguracji nie można cicho pomijać.
Opłata priorytetowa to łączna liczba lamport, nie cena
W formatach legacy i v0 opłata priorytetowa jest ustawiana przez SetComputeUnitPrice
jako mikro-lamporty na jednostkę obliczeniową, pomnożone przez limit jednostek
obliczeniowych. W v1 jest to bezwzględna suma w lamportach — bez mnożenia,
bez zaokrąglania. Nie przenoś arytmetyki per-CU. Całkowity wzór na opłatę pozostaje
niezmieniony: (signatures × lamports_per_signature) + priority_fee.
Instrukcje ComputeBudget są no-opami w v1
Transakcja v1 nie odrzuca instrukcji ComputeBudget — ignoruje je na potrzeby konfiguracji. Nadal są wykonywane jako udane no-opy, zużywając 150 jednostek obliczeniowych i jeden z 64 slotów instrukcji, nie mając żadnego wpływu na budżet. Usuń je podczas budowania transakcji v1 i przestań ich szukać przy odczytywaniu transakcji v1: wartości znajdują się w konfiguracji wiadomości.
Pola konfiguracji muszą być ustawiane jawnie
Najważniejsza zmiana behawioralna dla nadawców: w odróżnieniu od legacy i v0, transakcje v1 muszą jawnie ustawiać limit jednostek obliczeniowych oraz limit rozmiaru danych załadowanych kont — w przeciwnym razie transakcja zakończy się niepowodzeniem.
| Nieustawione pole | legacy / v0 | v1 | Objaw przy pominięciu |
|---|---|---|---|
| Limit jednostek obliczeniowych | 200k na instrukcję, maks. 1,4M | 0 CU | Natychmiastowe niepowodzenie — brak budżetu |
| Rozmiar danych załadowanych kont | 64 MiB | 0 bajtów | MaxLoadedAccountsDataSizeExceeded przy pierwszym załadowanym koncie |
| Opłata priorytetowa | 0 | 0 | — |
| Rozmiar sterty | 32 KiB | 32 KiB | — |
Zalecane podejście to jednorazowa symulacja z oboma limitami ustawionymi na
maksimum, a następnie zapisanie zwróconych wartości unitsConsumed i
loadedAccountsDataSize z powrotem do konfiguracji, zaokrąglając rozmiar danych
w górę do kolejnej strony 32 KiB dla zapasu (model kosztów bloków nalicza opłaty
w stronach 32 KiB, więc zapas poniżej granicy następnej strony jest bezpłatny).
Przygotowanie do v1
v1 zmienia odczytywanie transakcji, nie tylko ich wysyłanie. Po aktywacji v1
każdy klient wywołujący getTransaction lub getBlock bez wyrażenia zgody
zacznie zwracać błędy dla transakcji v1:
- Przekaż
maxSupportedTransactionVersion: 1— liczbę całkowitą JSON1, nie ciąg"1"— dogetTransactionigetBlock. Przekazanie0powoduje błędy dla transakcji v1 tak samo jak pominięcie parametru, więc baza kodu zaktualizowana podczas wdrożenia v0 nadal wymaga zmiany tej wartości. getTransactionzwraca błąd-32015dla transakcji v1, a jedna transakcja v1 powoduje niepowodzenie całej odpowiedzigetBlockz tym samym błędem — nie ma wyniku częściowego.blockSubscribeemitujeblock: nulli przestaje się przesuwać, więc konsument odczytujący to jako pusty blok będzie cicho zostawać w tyle od pierwszego slot z v1 wzwyż.getSignaturesForAddressnigdy nie sprawdza treści transakcji, więc podpisy v1 są wyświetlane normalnie.- Odpowiedzi z wyrażoną zgodą zawierają obiekt
transactionConfigw wiadomości dla transakcji v1 (całkowicie nieobecny w legacy i v0). Potoki obliczające opłaty priorytetowe lub limity obliczeniowe przez skanowanie instrukcji ComputeBudget będą cicho zgłaszać zero dla każdej transakcji v1. - Używaj
encoding: 'base64'podczas dekodowania transakcji po stronie klienta oraz dlasendTransaction/simulateTransactionz transakcjami powyżej 1 232 bajtów — kodowanie base58 nadal jest ograniczone do starego rozmiaru. - Obsługa w bibliotekach klienckich wymaga najnowszych wersji:
@solana/kit8.0+, skrzynki Rust z generacji Agave 4.2.x lub web3.js v3. web3.js v1 odczytuje v1 od wersji 1.99.0 wzwyż, ale nie może go budować ani wysyłać.
Pełny przewodnik migracji — w tym obsługa bibliotek klienckich, wykrywanie wersji w strumieniowaniu (Geyser/gRPC) i zachowanie symulacji — znajdziesz na stronie aktualizacji do formatu transakcji v1.
Is this page helpful?