Transakcje wersjonowane

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

Limitlegacyv0v1
Maksymalny rozmiar transakcji1 232 bajty1 232 bajty4 096 bajtów
Adresy kont~32, ograniczone rozmiarem64, przez tablice wyszukiwania64, bezpośrednio
Tablice wyszukiwania adresównieobsługiwaneobsługiwanenieobsługiwane
Limity zasobówInstrukcje ComputeBudgetInstrukcje ComputeBudgetkonfiguracja wiadomości

Przejdź do: Legacy · v0 · v1

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

PoleRozmiarOpis
num_signaturescompact-u16Liczba podpisów
signaturesnum_signatures x 64 bajtyPodpisy Ed25519
header3 bajtyMessageHeader — najwyższy bit pierwszego bajtu jest wyzerowany
num_account_keyscompact-u16Liczba kluczy kont
account_keysnum_account_keys x 32 bajtyKlucze publiczne, wszystkie bezpośrednio
recent_blockhash32 bajtySpecyfikator czasu życia
num_instructionscompact-u16Liczba instrukcji
instructionszmiennaKaż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

PoleRozmiarOpis
num_signaturescompact-u16Liczba podpisów
signaturesnum_signatures x 64 bajtyPodpisy Ed25519
0x801 bajtBajt prefiksu wersji — pierwszy bajt wiadomości
header3 bajtyMessageHeader (jak w legacy)
num_account_keyscompact-u16Liczba statycznych kluczy kont
static_account_keysnum_account_keys x 32 bajtyKlucze występujące dosłownie w transakcji
recent_blockhash32 bajtySpecyfikator czasu życia
num_instructionscompact-u16Liczba instrukcji
instructionszmiennaTen sam format co legacy
address_table_lookupscompact-u16 + zmiennaOdwołania ALT (patrz poniżej)

Każdy wpis w tabeli wyszukiwania adresów zawiera:

PoleRozmiarOpis
account_key32 bajtyKlucz publiczny konta ALT
writable_indexescompact-u16 + N x 1 bajtIndeksy do ALT dla kont z prawem zapisu
readonly_indexescompact-u16 + N x 1 bajtIndeksy 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

PoleRozmiarOpis
0x811 bajtBajt prefiksu wersji — pierwszy bajt transakcji
header3 bajtyMessageHeader (identyczny jak w legacy)
config_mask4 bajtyMaska bitowa u32 LE wskazująca, które wartości konfiguracji są obecne
recent_blockhash32 bajtySpecyfikator czasu życia
num_instructions1 bajtLicznik o stałej szerokości, maks. 64
num_addresses1 bajtLicznik o stałej szerokości, maks. 64
addressesN x 32 bajtyAdresy kont, wszystkie bezpośrednio — brak odwołań do tablic wyszukiwania
config_values0–20 bajtówJedna wartość na każdy ustawiony bit maski, w kolejności bitów (patrz niżej)
instruction_headersN x 4 bajtyNa instrukcję: program_id_index (u8), num_accounts (u8), data_len (u16 LE)
instruction_payloadszmiennaNa instrukcję: indeksy kont, następnie instruction data
signaturesN x 64 bajtyNa 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)PoleSzerokośćUwagi
0–1Opłata priorytetowau64Łączna liczba lamport — oba bity ustawiane razem
2Limit jednostek obliczeniowychu32
3Limit rozmiaru danych załadowanych kontu32
4Żądany rozmiar stertyu32

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 polelegacy / v0v1Objaw przy pominięciu
Limit jednostek obliczeniowych200k na instrukcję, maks. 1,4M0 CUNatychmiastowe niepowodzenie — brak budżetu
Rozmiar danych załadowanych kont64 MiB0 bajtówMaxLoadedAccountsDataSizeExceeded przy pierwszym załadowanym koncie
Opłata priorytetowa00
Rozmiar sterty32 KiB32 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: 1liczbę całkowitą JSON 1, nie ciąg "1" — do getTransaction i getBlock. Przekazanie 0 powoduje 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.
  • getTransaction zwraca błąd -32015 dla transakcji v1, a jedna transakcja v1 powoduje niepowodzenie całej odpowiedzi getBlock z tym samym błędem — nie ma wyniku częściowego.
  • blockSubscribe emituje block: null i przestaje się przesuwać, więc konsument odczytujący to jako pusty blok będzie cicho zostawać w tyle od pierwszego slot z v1 wzwyż.
  • getSignaturesForAddress nigdy nie sprawdza treści transakcji, więc podpisy v1 są wyświetlane normalnie.
  • Odpowiedzi z wyrażoną zgodą zawierają obiekt transactionConfig w 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 dla sendTransaction/simulateTransaction z transakcjami powyżej 1 232 bajtów — kodowanie base58 nadal jest ograniczone do starego rozmiaru.
  • Obsługa w bibliotekach klienckich wymaga najnowszych wersji: @solana/kit 8.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?